Update obsolete nbtree array preprocessing comments.
[pgsql.git] / meson.build
blob7150f85e0fb30001b612ec75f896d5a604c58182
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 = ''
271   if cc.get_id() != 'msvc'
272     # define before including <time.h> for getting localtime_r() etc. on MinGW
273     cppflags += '-D_POSIX_C_SOURCE'
274   endif
276   export_file_format = 'win'
277   export_file_suffix = 'def'
278   if cc.get_id() == 'msvc'
279     export_fmt = '/DEF:@0@'
280     mod_link_with_name = '@0@.lib'
281   else
282     export_fmt = '@0@'
283     mod_link_with_name = 'lib@0@.a'
284   endif
285   mod_link_args_fmt = ['@0@']
286   mod_link_with_dir = 'libdir'
288   shmem_kind = 'win32'
289   sema_kind = 'win32'
291   cdata.set('WIN32_STACK_RLIMIT', 4194304)
292   if cc.get_id() == 'msvc'
293     ldflags += '/INCREMENTAL:NO'
294     ldflags += '/STACK:@0@'.format(cdata.get('WIN32_STACK_RLIMIT'))
295     # ldflags += '/nxcompat' # generated by msbuild, should have it for ninja?
296   else
297     ldflags += '-Wl,--stack,@0@'.format(cdata.get('WIN32_STACK_RLIMIT'))
298     # Need to allow multiple definitions, we e.g. want to override getopt.
299     ldflags += '-Wl,--allow-multiple-definition'
300     # Ensure we get MSVC-like linking behavior.
301     ldflags += '-Wl,--disable-auto-import'
302   endif
304   os_deps += cc.find_library('ws2_32', required: true)
305   secur32_dep = cc.find_library('secur32', required: true)
306   backend_deps += secur32_dep
307   libpq_deps += secur32_dep
309   postgres_inc_d += 'src/include/port/win32'
310   if cc.get_id() == 'msvc'
311     postgres_inc_d += 'src/include/port/win32_msvc'
312   endif
314   windows = import('windows')
316 else
317   # XXX: Should we add an option to override the host_system as an escape
318   # hatch?
319   error('unknown host system: @0@'.format(host_system))
320 endif
324 ###############################################################
325 # Program paths
326 ###############################################################
328 # External programs
329 perl = find_program(get_option('PERL'), required: true, native: true)
330 python = find_program(get_option('PYTHON'), required: true, native: true)
331 flex = find_program(get_option('FLEX'), native: true, version: '>= 2.5.35')
332 bison = find_program(get_option('BISON'), native: true, version: '>= 2.3')
333 sed = find_program(get_option('SED'), 'sed', native: true, required: false)
334 prove = find_program(get_option('PROVE'), native: true, required: false)
335 tar = find_program(get_option('TAR'), native: true, required: false)
336 gzip = find_program(get_option('GZIP'), native: true, required: false)
337 program_lz4 = find_program(get_option('LZ4'), native: true, required: false)
338 openssl = find_program(get_option('OPENSSL'), native: true, required: false)
339 program_zstd = find_program(get_option('ZSTD'), native: true, required: false)
340 dtrace = find_program(get_option('DTRACE'), native: true, required: get_option('dtrace'))
341 missing = find_program('config/missing', native: true)
342 cp = find_program('cp', required: false, native: true)
343 xmllint_bin = find_program(get_option('XMLLINT'), native: true, required: false)
344 xsltproc_bin = find_program(get_option('XSLTPROC'), native: true, required: false)
346 bison_flags = []
347 if bison.found()
348   bison_version_c = run_command(bison, '--version', check: true)
349   # bison version string helpfully is something like
350   # >>bison (GNU bison) 3.8.1<<
351   bison_version = bison_version_c.stdout().split(' ')[3].split('\n')[0]
352   if bison_version.version_compare('>=3.0')
353     bison_flags += ['-Wno-deprecated']
354   endif
355 endif
356 bison_cmd = [bison, bison_flags, '-o', '@OUTPUT0@', '-d', '@INPUT@']
357 bison_kw = {
358   'output': ['@BASENAME@.c', '@BASENAME@.h'],
359   'command': bison_cmd,
362 flex_flags = []
363 if flex.found()
364   flex_version_c = run_command(flex, '--version', check: true)
365   flex_version = flex_version_c.stdout().split(' ')[1].split('\n')[0]
366 endif
367 flex_wrapper = files('src/tools/pgflex')
368 flex_cmd = [python, flex_wrapper,
369   '--builddir', '@BUILD_ROOT@',
370   '--srcdir', '@SOURCE_ROOT@',
371   '--privatedir', '@PRIVATE_DIR@',
372   '--flex', flex, '--perl', perl,
373   '-i', '@INPUT@', '-o', '@OUTPUT0@',
376 wget = find_program('wget', required: false, native: true)
377 wget_flags = ['-O', '@OUTPUT0@', '--no-use-server-timestamps']
379 install_files = files('src/tools/install_files')
383 ###############################################################
384 # Path to meson (for tests etc)
385 ###############################################################
387 # NB: this should really be part of meson, see
388 # https://github.com/mesonbuild/meson/issues/8511
389 meson_binpath_r = run_command(python, 'src/tools/find_meson', check: true)
391 if meson_binpath_r.stdout() == ''
392   error('huh, could not run find_meson.\nerrcode: @0@\nstdout: @1@\nstderr: @2@'.format(
393     meson_binpath_r.returncode(),
394     meson_binpath_r.stdout(),
395     meson_binpath_r.stderr()))
396 endif
398 meson_binpath_s = meson_binpath_r.stdout().split('\n')
399 meson_binpath_len = meson_binpath_s.length()
401 if meson_binpath_len < 1
402   error('unexpected introspect line @0@'.format(meson_binpath_r.stdout()))
403 endif
405 i = 0
406 meson_impl = ''
407 meson_binpath = ''
408 meson_args = []
409 foreach e : meson_binpath_s
410   if i == 0
411     meson_impl = e
412   elif i == 1
413     meson_binpath = e
414   else
415     meson_args += e
416   endif
417   i += 1
418 endforeach
420 if meson_impl not in ['muon', 'meson']
421   error('unknown meson implementation "@0@"'.format(meson_impl))
422 endif
424 meson_bin = find_program(meson_binpath, native: true)
428 ###############################################################
429 # Option Handling
430 ###############################################################
432 cdata.set('USE_ASSERT_CHECKING', get_option('cassert') ? 1 : false)
433 cdata.set('USE_INJECTION_POINTS', get_option('injection_points') ? 1 : false)
435 blocksize = get_option('blocksize').to_int() * 1024
437 if get_option('segsize_blocks') != 0
438   if get_option('segsize') != 1
439     warning('both segsize and segsize_blocks specified, segsize_blocks wins')
440   endif
442   segsize = get_option('segsize_blocks')
443 else
444   segsize = (get_option('segsize') * 1024 * 1024 * 1024) / blocksize
445 endif
447 cdata.set('BLCKSZ', blocksize, description:
448 '''Size of a disk block --- this also limits the size of a tuple. You can set
449    it bigger if you need bigger tuples (although TOAST should reduce the need
450    to have large tuples, since fields can be spread across multiple tuples).
451    BLCKSZ must be a power of 2. The maximum possible value of BLCKSZ is
452    currently 2^15 (32768). This is determined by the 15-bit widths of the
453    lp_off and lp_len fields in ItemIdData (see include/storage/itemid.h).
454    Changing BLCKSZ requires an initdb.''')
456 cdata.set('XLOG_BLCKSZ', get_option('wal_blocksize').to_int() * 1024)
457 cdata.set('RELSEG_SIZE', segsize)
458 cdata.set('DEF_PGPORT', get_option('pgport'))
459 cdata.set_quoted('DEF_PGPORT_STR', get_option('pgport').to_string())
460 cdata.set_quoted('PG_KRB_SRVNAM', get_option('krb_srvnam'))
461 if get_option('system_tzdata') != ''
462   cdata.set_quoted('SYSTEMTZDIR', get_option('system_tzdata'))
463 endif
467 ###############################################################
468 # Directories
469 ###############################################################
471 # These are set by the equivalent --xxxdir configure options.  We
472 # append "postgresql" to some of them, if the string does not already
473 # contain "pgsql" or "postgres", in order to avoid directory clutter.
475 pkg = 'postgresql'
477 dir_prefix = get_option('prefix')
479 dir_prefix_contains_pg = (dir_prefix.contains('pgsql') or dir_prefix.contains('postgres'))
481 dir_bin = get_option('bindir')
483 dir_data = get_option('datadir')
484 if not (dir_prefix_contains_pg or dir_data.contains('pgsql') or dir_data.contains('postgres'))
485   dir_data = dir_data / pkg
486 endif
488 dir_sysconf = get_option('sysconfdir')
489 if not (dir_prefix_contains_pg or dir_sysconf.contains('pgsql') or dir_sysconf.contains('postgres'))
490   dir_sysconf = dir_sysconf / pkg
491 endif
493 dir_lib = get_option('libdir')
495 dir_lib_pkg = dir_lib
496 if not (dir_prefix_contains_pg or dir_lib_pkg.contains('pgsql') or dir_lib_pkg.contains('postgres'))
497   dir_lib_pkg = dir_lib_pkg / pkg
498 endif
500 dir_pgxs = dir_lib_pkg / 'pgxs'
502 dir_include = get_option('includedir')
504 dir_include_pkg = dir_include
505 dir_include_pkg_rel = ''
506 if not (dir_prefix_contains_pg or dir_include_pkg.contains('pgsql') or dir_include_pkg.contains('postgres'))
507   dir_include_pkg = dir_include_pkg / pkg
508   dir_include_pkg_rel = pkg
509 endif
511 dir_man = get_option('mandir')
513 # FIXME: These used to be separately configurable - worth adding?
514 dir_doc = get_option('datadir') / 'doc'
515 if not (dir_prefix_contains_pg or dir_doc.contains('pgsql') or dir_doc.contains('postgres'))
516   dir_doc = dir_doc / pkg
517 endif
518 dir_doc_html = dir_doc / 'html'
520 dir_locale = get_option('localedir')
523 # Derived values
524 dir_bitcode = dir_lib_pkg / 'bitcode'
525 dir_include_internal = dir_include_pkg / 'internal'
526 dir_include_server = dir_include_pkg / 'server'
527 dir_include_extension = dir_include_server / 'extension'
528 dir_data_extension = dir_data / 'extension'
529 dir_doc_extension = dir_doc / 'extension'
533 ###############################################################
534 # Search paths, preparation for compiler tests
536 # NB: Arguments added later are not automatically used for subsequent
537 # configuration-time checks (so they are more isolated). If they should be
538 # used, they need to be added to test_c_args as well.
539 ###############################################################
541 postgres_inc = [include_directories(postgres_inc_d)]
542 test_lib_d = postgres_lib_d
543 test_c_args = cppflags + cflags
547 ###############################################################
548 # Library: bsd-auth
549 ###############################################################
551 bsd_authopt = get_option('bsd_auth')
552 bsd_auth = not_found_dep
553 if cc.check_header('bsd_auth.h', required: bsd_authopt,
554     args: test_c_args, include_directories: postgres_inc)
555   cdata.set('USE_BSD_AUTH', 1)
556   bsd_auth = declare_dependency()
557 endif
561 ###############################################################
562 # Library: bonjour
564 # For now don't search for DNSServiceRegister in a library - only Apple's
565 # Bonjour implementation, which is always linked, works.
566 ###############################################################
568 bonjouropt = get_option('bonjour')
569 bonjour = not_found_dep
570 if cc.check_header('dns_sd.h', required: bonjouropt,
571     args: test_c_args, include_directories: postgres_inc) and \
572    cc.has_function('DNSServiceRegister',
573     args: test_c_args, include_directories: postgres_inc)
574   cdata.set('USE_BONJOUR', 1)
575   bonjour = declare_dependency()
576 endif
580 ###############################################################
581 # Option: docs in HTML and man page format
582 ###############################################################
584 docs_opt = get_option('docs')
585 docs_dep = not_found_dep
586 if not docs_opt.disabled()
587   if xmllint_bin.found() and xsltproc_bin.found()
588     docs_dep = declare_dependency()
589   elif docs_opt.enabled()
590     error('missing required tools (xmllint and xsltproc needed) for docs in HTML / man page format')
591   endif
592 endif
596 ###############################################################
597 # Option: docs in PDF format
598 ###############################################################
600 docs_pdf_opt = get_option('docs_pdf')
601 docs_pdf_dep = not_found_dep
602 if not docs_pdf_opt.disabled()
603   fop = find_program(get_option('FOP'), native: true, required: docs_pdf_opt)
604   if xmllint_bin.found() and xsltproc_bin.found() and fop.found()
605     docs_pdf_dep = declare_dependency()
606   elif docs_pdf_opt.enabled()
607     error('missing required tools for docs in PDF format')
608   endif
609 endif
613 ###############################################################
614 # Library: GSSAPI
615 ###############################################################
617 gssapiopt = get_option('gssapi')
618 krb_srvtab = ''
619 have_gssapi = false
620 if not gssapiopt.disabled()
621   gssapi = dependency('krb5-gssapi', required: false)
622   have_gssapi = gssapi.found()
624   if have_gssapi
625       gssapi_deps = [gssapi]
626   elif not have_gssapi
627     # Hardcoded lookup for gssapi. This is necessary as gssapi on windows does
628     # not install neither pkg-config nor cmake dependency information.
629     if host_system == 'windows'
630       is_64  = cc.sizeof('void *', args: test_c_args) == 8
631       if is_64
632         gssapi_search_libs = ['gssapi64', 'krb5_64', 'comerr64']
633       else
634         gssapi_search_libs = ['gssapi32', 'krb5_32', 'comerr32']
635       endif
636     else
637       gssapi_search_libs = ['gssapi_krb5']
638     endif
640     gssapi_deps = []
641     foreach libname : gssapi_search_libs
642       lib = cc.find_library(libname, dirs: test_lib_d, required: false)
643       if lib.found()
644         have_gssapi = true
645         gssapi_deps += lib
646       endif
647     endforeach
649     if have_gssapi
650       # Meson before 0.57.0 did not support using check_header() etc with
651       # declare_dependency(). Thus the tests below use the library looked up
652       # above.  Once we require a newer meson version, we can simplify.
653       gssapi = declare_dependency(dependencies: gssapi_deps)
654     endif
655   endif
657   if not have_gssapi
658   elif cc.check_header('gssapi/gssapi.h', dependencies: gssapi_deps, required: false,
659       args: test_c_args, include_directories: postgres_inc)
660     cdata.set('HAVE_GSSAPI_GSSAPI_H', 1)
661   elif cc.check_header('gssapi.h', dependencies: gssapi_deps, required: gssapiopt,
662       args: test_c_args, include_directories: postgres_inc)
663     cdata.set('HAVE_GSSAPI_H', 1)
664   else
665     have_gssapi = false
666   endif
668   if not have_gssapi
669   elif cc.check_header('gssapi/gssapi_ext.h', dependencies: gssapi_deps, required: false,
670       args: test_c_args, include_directories: postgres_inc)
671     cdata.set('HAVE_GSSAPI_GSSAPI_EXT_H', 1)
672   elif cc.check_header('gssapi_ext.h', dependencies: gssapi_deps, required: gssapiopt,
673       args: test_c_args, include_directories: postgres_inc)
674     cdata.set('HAVE_GSSAPI_EXT_H', 1)
675   else
676     have_gssapi = false
677   endif
679   if not have_gssapi
680   elif cc.has_function('gss_store_cred_into', dependencies: gssapi_deps,
681       args: test_c_args, include_directories: postgres_inc)
682     cdata.set('ENABLE_GSS', 1)
684     krb_srvtab = 'FILE:/@0@/krb5.keytab)'.format(get_option('sysconfdir'))
685     cdata.set_quoted('PG_KRB_SRVTAB', krb_srvtab)
686   elif gssapiopt.enabled()
687     error('''could not find function 'gss_store_cred_into' required for GSSAPI''')
688   else
689     have_gssapi = false
690   endif
692   if not have_gssapi and gssapiopt.enabled()
693     error('dependency lookup for gssapi failed')
694   endif
696 endif
697 if not have_gssapi
698   gssapi = not_found_dep
699 endif
703 ###############################################################
704 # Library: ldap
705 ###############################################################
707 ldapopt = get_option('ldap')
708 if ldapopt.disabled()
709   ldap = not_found_dep
710   ldap_r = not_found_dep
711 elif host_system == 'windows'
712   ldap = cc.find_library('wldap32', required: ldapopt)
713   ldap_r = ldap
714 else
715   # macos framework dependency is buggy for ldap (one can argue whether it's
716   # Apple's or meson's fault), leading to an endless recursion with ldap.h
717   # including itself. See https://github.com/mesonbuild/meson/issues/10002
718   # Luckily we only need pkg-config support, so the workaround isn't
719   # complicated.
720   ldap = dependency('ldap', method: 'pkg-config', required: false)
721   ldap_r = ldap
723   # Before 2.5 openldap didn't have a pkg-config file, and it might not be
724   # installed
725   if not ldap.found()
726     ldap = cc.find_library('ldap', required: ldapopt, dirs: test_lib_d,
727       has_headers: 'ldap.h', header_include_directories: postgres_inc)
729     # The separate ldap_r library only exists in OpenLDAP < 2.5, and if we
730     # have 2.5 or later, we shouldn't even probe for ldap_r (we might find a
731     # library from a separate OpenLDAP installation).  The most reliable
732     # way to check that is to check for a function introduced in 2.5.
733     if not ldap.found()
734       # don't have ldap, we shouldn't check for ldap_r
735     elif cc.has_function('ldap_verify_credentials',
736         dependencies: ldap, args: test_c_args)
737       ldap_r = ldap # ldap >= 2.5, no need for ldap_r
738     else
740       # Use ldap_r for FE if available, else assume ldap is thread-safe.
741       ldap_r = cc.find_library('ldap_r', required: false, dirs: test_lib_d,
742         has_headers: 'ldap.h', header_include_directories: postgres_inc)
743       if not ldap_r.found()
744         ldap_r = ldap
745       else
746         # On some platforms ldap_r fails to link without PTHREAD_LIBS.
747         ldap_r = declare_dependency(dependencies: [ldap_r, thread_dep])
748       endif
750       # PostgreSQL sometimes loads libldap_r and plain libldap into the same
751       # process.  Check for OpenLDAP versions known not to tolerate doing so;
752       # assume non-OpenLDAP implementations are safe.  The dblink test suite
753       # exercises the hazardous interaction directly.
754       compat_test_code = '''
755 #include <ldap.h>
756 #if !defined(LDAP_VENDOR_VERSION) || \
757      (defined(LDAP_API_FEATURE_X_OPENLDAP) && \
758       LDAP_VENDOR_VERSION >= 20424 && LDAP_VENDOR_VERSION <= 20431)
759 choke me
760 #endif
762       if not cc.compiles(compat_test_code,
763           name: 'LDAP implementation compatible',
764           dependencies: ldap, args: test_c_args)
765         warning('''
766 *** With OpenLDAP versions 2.4.24 through 2.4.31, inclusive, each backend
767 *** process that loads libpq (via WAL receiver, dblink, or postgres_fdw) and
768 *** also uses LDAP will crash on exit.''')
769       endif
770     endif
771   endif
773   if ldap.found() and cc.has_function('ldap_initialize',
774       dependencies: ldap, args: test_c_args)
775     cdata.set('HAVE_LDAP_INITIALIZE', 1)
776   endif
777 endif
779 if ldap.found()
780   assert(ldap_r.found())
781   cdata.set('USE_LDAP', 1)
782 else
783   assert(not ldap_r.found())
784 endif
788 ###############################################################
789 # Library: LLVM
790 ###############################################################
792 llvmopt = get_option('llvm')
793 llvm = not_found_dep
794 if add_languages('cpp', required: llvmopt, native: false)
795   llvm = dependency('llvm', version: '>=10', method: 'config-tool', required: llvmopt)
797   if llvm.found()
799     cdata.set('USE_LLVM', 1)
801     cpp = meson.get_compiler('cpp')
803     llvm_binpath = llvm.get_variable(configtool: 'bindir')
805     ccache = find_program('ccache', native: true, required: false)
807     # Some distros put LLVM and clang in different paths, so fallback to
808     # find via PATH, too.
809     clang = find_program(llvm_binpath / 'clang', 'clang', required: true)
810   endif
811 elif llvmopt.auto()
812   message('llvm requires a C++ compiler')
813 endif
817 ###############################################################
818 # Library: icu
819 ###############################################################
821 icuopt = get_option('icu')
822 if not icuopt.disabled()
823   icu = dependency('icu-uc', required: false)
824   if icu.found()
825     icu_i18n = dependency('icu-i18n', required: true)
826   endif
828   # Unfortunately the dependency is named differently with cmake
829   if not icu.found() # combine with above once meson 0.60.0 is required
830     icu = dependency('ICU', required: icuopt,
831                      components: ['uc'], modules: ['ICU::uc'], method: 'cmake')
832     if icu.found()
833       icu_i18n = dependency('ICU', required: true,
834                             components: ['i18n'], modules: ['ICU::i18n'])
835     endif
836   endif
838   if icu.found()
839     cdata.set('USE_ICU', 1)
840   else
841     icu_i18n = not_found_dep
842   endif
844 else
845   icu = not_found_dep
846   icu_i18n = not_found_dep
847 endif
851 ###############################################################
852 # Library: libxml
853 ###############################################################
855 libxmlopt = get_option('libxml')
856 if not libxmlopt.disabled()
857   libxml = dependency('libxml-2.0', required: false, version: '>= 2.6.23')
858   # Unfortunately the dependency is named differently with cmake
859   if not libxml.found() # combine with above once meson 0.60.0 is required
860     libxml = dependency('LibXml2', required: libxmlopt, version: '>= 2.6.23',
861       method: 'cmake')
862   endif
864   if libxml.found()
865     cdata.set('USE_LIBXML', 1)
866   endif
867 else
868   libxml = not_found_dep
869 endif
873 ###############################################################
874 # Library: libxslt
875 ###############################################################
877 libxsltopt = get_option('libxslt')
878 if not libxsltopt.disabled()
879   libxslt = dependency('libxslt', required: false)
880   # Unfortunately the dependency is named differently with cmake
881   if not libxslt.found() # combine with above once meson 0.60.0 is required
882     libxslt = dependency('LibXslt', required: libxsltopt, method: 'cmake')
883   endif
885   if libxslt.found()
886     cdata.set('USE_LIBXSLT', 1)
887   endif
888 else
889   libxslt = not_found_dep
890 endif
894 ###############################################################
895 # Library: lz4
896 ###############################################################
898 lz4opt = get_option('lz4')
899 if not lz4opt.disabled()
900   lz4 = dependency('liblz4', required: false)
901   # Unfortunately the dependency is named differently with cmake
902   if not lz4.found() # combine with above once meson 0.60.0 is required
903     lz4 = dependency('lz4', required: lz4opt,
904                      method: 'cmake', modules: ['LZ4::lz4_shared'],
905                     )
906   endif
908   if lz4.found()
909     cdata.set('USE_LZ4', 1)
910     cdata.set('HAVE_LIBLZ4', 1)
911   endif
913 else
914   lz4 = not_found_dep
915 endif
919 ###############################################################
920 # Library: Tcl (for pltcl)
922 # NB: tclConfig.sh is used in autoconf build for getting
923 # TCL_SHARED_BUILD, TCL_INCLUDE_SPEC, TCL_LIBS and TCL_LIB_SPEC
924 # variables. For now we have not seen a need to copy
925 # that behaviour to the meson build.
926 ###############################################################
928 tclopt = get_option('pltcl')
929 tcl_version = get_option('tcl_version')
930 tcl_dep = not_found_dep
931 if not tclopt.disabled()
933   # via pkg-config
934   tcl_dep = dependency(tcl_version, required: false)
936   if not tcl_dep.found()
937     tcl_dep = cc.find_library(tcl_version,
938       required: tclopt,
939       dirs: test_lib_d)
940   endif
942   if not cc.has_header('tcl.h', dependencies: tcl_dep, required: tclopt)
943     tcl_dep = not_found_dep
944   endif
945 endif
949 ###############################################################
950 # Library: pam
951 ###############################################################
953 pamopt = get_option('pam')
954 if not pamopt.disabled()
955   pam = dependency('pam', required: false)
957   if not pam.found()
958     pam = cc.find_library('pam', required: pamopt, dirs: test_lib_d)
959   endif
961   if pam.found()
962     pam_header_found = false
964     # header file <security/pam_appl.h> or <pam/pam_appl.h> is required for PAM.
965     if cc.check_header('security/pam_appl.h', dependencies: pam, required: false,
966         args: test_c_args, include_directories: postgres_inc)
967       cdata.set('HAVE_SECURITY_PAM_APPL_H', 1)
968       pam_header_found = true
969     elif cc.check_header('pam/pam_appl.h', dependencies: pam, required: pamopt,
970         args: test_c_args, include_directories: postgres_inc)
971       cdata.set('HAVE_PAM_PAM_APPL_H', 1)
972       pam_header_found = true
973     endif
975     if pam_header_found
976       cdata.set('USE_PAM', 1)
977     else
978       pam = not_found_dep
979     endif
980   endif
981 else
982   pam = not_found_dep
983 endif
987 ###############################################################
988 # Library: Perl (for plperl)
989 ###############################################################
991 perlopt = get_option('plperl')
992 perl_dep = not_found_dep
993 if not perlopt.disabled()
994   perl_may_work = true
996   # First verify that perl has the necessary dependencies installed
997   perl_mods = run_command(
998     [perl,
999      '-MConfig', '-MOpcode', '-MExtUtils::Embed', '-MExtUtils::ParseXS',
1000      '-e', ''],
1001     check: false)
1002   if perl_mods.returncode() != 0
1003     perl_may_work = false
1004     perl_msg = 'perl installation does not have the required modules'
1005   endif
1007   # Then inquire perl about its configuration
1008   if perl_may_work
1009     perl_conf_cmd = [perl, '-MConfig', '-e', 'print $Config{$ARGV[0]}']
1010     perlversion = run_command(perl_conf_cmd, 'api_versionstring', check: true).stdout()
1011     archlibexp = run_command(perl_conf_cmd, 'archlibexp', check: true).stdout()
1012     privlibexp = run_command(perl_conf_cmd, 'privlibexp', check: true).stdout()
1013     useshrplib = run_command(perl_conf_cmd, 'useshrplib', check: true).stdout()
1015     perl_inc_dir = '@0@/CORE'.format(archlibexp)
1017     if perlversion.version_compare('< 5.14')
1018       perl_may_work = false
1019       perl_msg = 'Perl version 5.14 or later is required, but this is @0@'.format(perlversion)
1020     elif useshrplib != 'true'
1021       perl_may_work = false
1022       perl_msg = 'need a shared perl'
1023     endif
1024   endif
1026   if perl_may_work
1027     # On most platforms, archlibexp is also where the Perl include files live ...
1028     perl_ccflags = ['-I@0@'.format(perl_inc_dir)]
1029     # ... but on newer macOS versions, we must use -iwithsysroot to look
1030     # under sysroot
1031     if not fs.is_file('@0@/perl.h'.format(perl_inc_dir)) and \
1032        fs.is_file('@0@@1@/perl.h'.format(pg_sysroot, perl_inc_dir))
1033       perl_ccflags = ['-iwithsysroot', perl_inc_dir]
1034     endif
1036     # check compiler finds header
1037     if not cc.has_header('perl.h', required: false,
1038         args: test_c_args + perl_ccflags, include_directories: postgres_inc)
1039       perl_may_work = false
1040       perl_msg = 'missing perl.h'
1041     endif
1042   endif
1044   if perl_may_work
1045     perl_ccflags_r = run_command(perl_conf_cmd, 'ccflags', check: true).stdout()
1047     # See comments for PGAC_CHECK_PERL_EMBED_CCFLAGS in perl.m4
1048     foreach flag : perl_ccflags_r.split(' ')
1049       if flag.startswith('-D') and \
1050           (not flag.startswith('-D_') or flag == '_USE_32BIT_TIME_T')
1051         perl_ccflags += flag
1052       endif
1053     endforeach
1055     if host_system == 'windows'
1056       perl_ccflags += ['-DPLPERL_HAVE_UID_GID']
1058       if cc.get_id() == 'msvc'
1059         # prevent binary mismatch between MSVC built plperl and Strawberry or
1060         # msys ucrt perl libraries
1061         perl_v = run_command(perl, '-V').stdout()
1062         if not perl_v.contains('USE_THREAD_SAFE_LOCALE')
1063           perl_ccflags += ['-DNO_THREAD_SAFE_LOCALE']
1064         endif
1065       endif
1066     endif
1068     message('CCFLAGS recommended by perl: @0@'.format(perl_ccflags_r))
1069     message('CCFLAGS for embedding perl: @0@'.format(' '.join(perl_ccflags)))
1071     # We are after Embed's ldopts, but without the subset mentioned in
1072     # Config's ccdlflags and ldflags.  (Those are the choices of those who
1073     # built the Perl installation, which are not necessarily appropriate
1074     # for building PostgreSQL.)
1075     perl_ldopts = run_command(perl, '-e', '''
1076 use ExtUtils::Embed;
1077 use Text::ParseWords;
1078 # tell perl to suppress including these in ldopts
1079 *ExtUtils::Embed::_ldflags =*ExtUtils::Embed::_ccdlflags = sub { return ""; };
1080 # adding an argument to ldopts makes it return a value instead of printing
1081 # print one of these per line so splitting will preserve spaces in file names.
1082 # shellwords eats backslashes, so we need to escape them.
1083 (my $opts = ldopts(undef)) =~ s!\\!\\\\!g;
1084 print "$_\n" foreach shellwords($opts);
1085 ''',
1086      check: true).stdout().strip().split('\n')
1088     message('LDFLAGS for embedding perl: "@0@"'.format(' '.join(perl_ldopts)))
1090     perl_dep_int = declare_dependency(
1091       compile_args: perl_ccflags,
1092       link_args: perl_ldopts,
1093       version: perlversion,
1094     )
1096     # While we're at it, check that we can link to libperl.
1097     # On most platforms, if perl.h is there then libperl.so will be too, but
1098     # at this writing Debian packages them separately.
1099     perl_link_test = '''
1100 /* see plperl.h */
1101 #ifdef _MSC_VER
1102 #define __inline__ inline
1103 #endif
1104 #include <EXTERN.h>
1105 #include <perl.h>
1106 int main(void)
1108 perl_alloc();
1109 }'''
1110     if not cc.links(perl_link_test, name: 'libperl',
1111           args: test_c_args + perl_ccflags + perl_ldopts,
1112           include_directories: postgres_inc)
1113       perl_may_work = false
1114       perl_msg = 'missing libperl'
1115     endif
1117   endif # perl_may_work
1119   if perl_may_work
1120     perl_dep = perl_dep_int
1121   else
1122     if perlopt.enabled()
1123       error('dependency plperl failed: @0@'.format(perl_msg))
1124     else
1125       message('disabling optional dependency plperl: @0@'.format(perl_msg))
1126     endif
1127   endif
1128 endif
1132 ###############################################################
1133 # Library: Python (for plpython)
1134 ###############################################################
1136 pyopt = get_option('plpython')
1137 python3_dep = not_found_dep
1138 if not pyopt.disabled()
1139   pm = import('python')
1140   python3_inst = pm.find_installation(python.path(), required: pyopt)
1141   if python3_inst.found()
1142     python3_dep = python3_inst.dependency(embed: true, required: pyopt)
1143     # Remove this check after we depend on Meson >= 1.1.0
1144     if not cc.check_header('Python.h', dependencies: python3_dep, required: pyopt, include_directories: postgres_inc)
1145       python3_dep = not_found_dep
1146     endif
1147   endif
1148 endif
1152 ###############################################################
1153 # Library: Readline
1154 ###############################################################
1156 if not get_option('readline').disabled()
1157   libedit_preferred = get_option('libedit_preferred')
1158   # Set the order of readline dependencies.
1159   # cc.find_library breaks and throws on the first dependency which
1160   # is marked as required=true and can't be found. Thus, we only mark
1161   # the last dependency to look up as required, to not throw too early.
1162   check_readline_deps = [
1163     {
1164       'name': libedit_preferred ? 'libedit' : 'readline',
1165       'required': false
1166     },
1167     {
1168       'name': libedit_preferred ? 'readline' : 'libedit',
1169       'required': get_option('readline')
1170     }
1171   ]
1173   foreach readline_dep : check_readline_deps
1174     readline = dependency(readline_dep['name'], required: false)
1175     if not readline.found()
1176       readline = cc.find_library(readline_dep['name'],
1177         required: readline_dep['required'],
1178         dirs: test_lib_d)
1179     endif
1180     if readline.found()
1181       break
1182     endif
1183   endforeach
1185   if readline.found()
1186     cdata.set('HAVE_LIBREADLINE', 1)
1188     editline_prefix = {
1189       'header_prefix': 'editline/',
1190       'flag_prefix': 'EDITLINE_',
1191     }
1192     readline_prefix = {
1193       'header_prefix': 'readline/',
1194       'flag_prefix': 'READLINE_',
1195     }
1196     default_prefix = {
1197       'header_prefix': '',
1198       'flag_prefix': '',
1199     }
1201     # Set the order of prefixes
1202     prefixes = libedit_preferred ? \
1203       [editline_prefix, default_prefix, readline_prefix] : \
1204       [readline_prefix, default_prefix, editline_prefix]
1206     at_least_one_header_found = false
1207     foreach header : ['history', 'readline']
1208       is_found = false
1209       foreach prefix : prefixes
1210         header_file = '@0@@1@.h'.format(prefix['header_prefix'], header)
1211         # Check history.h and readline.h
1212         if not is_found and cc.has_header(header_file,
1213             args: test_c_args, include_directories: postgres_inc,
1214             dependencies: [readline], required: false)
1215           if header == 'readline'
1216             readline_h = header_file
1217           endif
1218           cdata.set('HAVE_@0@@1@_H'.format(prefix['flag_prefix'], header).to_upper(), 1)
1219           is_found = true
1220           at_least_one_header_found = true
1221         endif
1222       endforeach
1223     endforeach
1225     if not at_least_one_header_found
1226       error('''readline header not found
1227 If you have @0@ already installed, see meson-logs/meson-log.txt for details on the
1228 failure. It is possible the compiler isn't looking in the proper directory.
1229 Use -Dreadline=disabled to disable readline support.'''.format(readline_dep))
1230     endif
1232     check_funcs = [
1233       'append_history',
1234       'history_truncate_file',
1235       'rl_completion_matches',
1236       'rl_filename_completion_function',
1237       'rl_reset_screen_size',
1238       'rl_variable_bind',
1239     ]
1241     foreach func : check_funcs
1242       found = cc.has_function(func, dependencies: [readline],
1243         args: test_c_args, include_directories: postgres_inc)
1244       cdata.set('HAVE_' + func.to_upper(), found ? 1 : false)
1245     endforeach
1247     check_vars = [
1248       'rl_completion_suppress_quote',
1249       'rl_filename_quote_characters',
1250       'rl_filename_quoting_function',
1251     ]
1253     foreach var : check_vars
1254       cdata.set('HAVE_' + var.to_upper(),
1255         cc.has_header_symbol(readline_h, var,
1256           args: test_c_args, include_directories: postgres_inc,
1257           prefix: '#include <stdio.h>',
1258           dependencies: [readline]) ? 1 : false)
1259     endforeach
1261     # If found via cc.find_library() ensure headers are found when using the
1262     # dependency. On meson < 0.57 one cannot do compiler checks using the
1263     # dependency returned by declare_dependency(), so we can't do this above.
1264     if readline.type_name() == 'library'
1265       readline = declare_dependency(dependencies: readline,
1266         include_directories: postgres_inc)
1267     endif
1269     # On windows with mingw readline requires auto-import to successfully
1270     # link, as the headers don't use declspec(dllimport)
1271     if host_system == 'windows' and cc.get_id() != 'msvc'
1272       readline = declare_dependency(dependencies: readline,
1273         link_args: '-Wl,--enable-auto-import')
1274     endif
1275   endif
1277   # XXX: Figure out whether to implement mingw warning equivalent
1278 else
1279   readline = not_found_dep
1280 endif
1284 ###############################################################
1285 # Library: selinux
1286 ###############################################################
1288 selinux = not_found_dep
1289 selinuxopt = get_option('selinux')
1290 if meson.version().version_compare('>=0.59')
1291   selinuxopt = selinuxopt.disable_auto_if(host_system != 'linux')
1292 endif
1293 selinux = dependency('libselinux', required: selinuxopt, version: '>= 2.1.10')
1294 cdata.set('HAVE_LIBSELINUX',
1295   selinux.found() ? 1 : false)
1299 ###############################################################
1300 # Library: systemd
1301 ###############################################################
1303 systemd = not_found_dep
1304 systemdopt = get_option('systemd')
1305 if meson.version().version_compare('>=0.59')
1306   systemdopt = systemdopt.disable_auto_if(host_system != 'linux')
1307 endif
1308 systemd = dependency('libsystemd', required: systemdopt)
1309 cdata.set('USE_SYSTEMD', systemd.found() ? 1 : false)
1313 ###############################################################
1314 # Library: SSL
1315 ###############################################################
1317 ssl = not_found_dep
1318 ssl_library = 'none'
1319 sslopt = get_option('ssl')
1321 if sslopt == 'auto' and auto_features.disabled()
1322   sslopt = 'none'
1323 endif
1325 if sslopt in ['auto', 'openssl']
1326   openssl_required = (sslopt == 'openssl')
1328   # Try to find openssl via pkg-config et al, if that doesn't work
1329   # (e.g. because it's provided as part of the OS, like on FreeBSD), look for
1330   # the library names that we know about.
1332   # via pkg-config et al
1333   ssl = dependency('openssl', required: false)
1334   # only meson >= 0.57 supports declare_dependency() in cc.has_function(), so
1335   # we pass cc.find_library() results if necessary
1336   ssl_int = []
1338   # via library + headers
1339   if not ssl.found()
1340     ssl_lib = cc.find_library('ssl',
1341       dirs: test_lib_d,
1342       header_include_directories: postgres_inc,
1343       has_headers: ['openssl/ssl.h', 'openssl/err.h'],
1344       required: openssl_required)
1345     crypto_lib = cc.find_library('crypto',
1346       dirs: test_lib_d,
1347       required: openssl_required)
1348     if ssl_lib.found() and crypto_lib.found()
1349       ssl_int = [ssl_lib, crypto_lib]
1350       ssl = declare_dependency(dependencies: ssl_int, include_directories: postgres_inc)
1351     endif
1352   elif cc.has_header('openssl/ssl.h', args: test_c_args, dependencies: ssl, required: openssl_required) and \
1353        cc.has_header('openssl/err.h', args: test_c_args, dependencies: ssl, required: openssl_required)
1354     ssl_int = [ssl]
1355   else
1356     ssl = not_found_dep
1357   endif
1359   if ssl.found()
1360     check_funcs = [
1361       ['CRYPTO_new_ex_data', {'required': true}],
1362       ['SSL_new', {'required': true}],
1364       # Functions introduced in OpenSSL 1.1.0. We used to check for
1365       # OPENSSL_VERSION_NUMBER, but that didn't work with 1.1.0, because LibreSSL
1366       # defines OPENSSL_VERSION_NUMBER to claim version 2.0.0, even though it
1367       # doesn't have these OpenSSL 1.1.0 functions. So check for individual
1368       # functions.
1369       ['OPENSSL_init_ssl', {'required': true}],
1371       # Function introduced in OpenSSL 1.0.2, not in LibreSSL.
1372       ['SSL_CTX_set_cert_cb'],
1374       # Function introduced in OpenSSL 1.1.1, not in LibreSSL.
1375       ['X509_get_signature_info'],
1376       ['SSL_CTX_set_num_tickets'],
1377     ]
1379     are_openssl_funcs_complete = true
1380     foreach c : check_funcs
1381       func = c.get(0)
1382       val = cc.has_function(func, args: test_c_args, dependencies: ssl_int)
1383       required = c.get(1, {}).get('required', false)
1384       if required and not val
1385         are_openssl_funcs_complete = false
1386         if openssl_required
1387           error('openssl function @0@ is required'.format(func))
1388         endif
1389         break
1390       elif not required
1391         cdata.set('HAVE_' + func.to_upper(), val ? 1 : false)
1392       endif
1393     endforeach
1395     if are_openssl_funcs_complete
1396       cdata.set('USE_OPENSSL', 1,
1397                 description: 'Define to 1 to build with OpenSSL support. (-Dssl=openssl)')
1398       cdata.set('OPENSSL_API_COMPAT', '0x10100000L',
1399                 description: 'Define to the OpenSSL API version in use. This avoids deprecation warnings from newer OpenSSL versions.')
1400       ssl_library = 'openssl'
1401     else
1402       ssl = not_found_dep
1403     endif
1404   endif
1405 endif
1407 if sslopt == 'auto' and auto_features.enabled() and not ssl.found()
1408   error('no SSL library found')
1409 endif
1413 ###############################################################
1414 # Library: uuid
1415 ###############################################################
1417 uuidopt = get_option('uuid')
1418 if uuidopt != 'none'
1419   uuidname = uuidopt.to_upper()
1420   if uuidopt == 'e2fs'
1421     uuid = dependency('uuid', required: true)
1422     uuidfunc = 'uuid_generate'
1423     uuidheader = 'uuid/uuid.h'
1424   elif uuidopt == 'bsd'
1425     # libc should have uuid function
1426     uuid = declare_dependency()
1427     uuidfunc = 'uuid_to_string'
1428     uuidheader = 'uuid.h'
1429   elif uuidopt == 'ossp'
1430     # In upstream, the package and library is called just 'uuid', but many
1431     # distros change it to 'ossp-uuid'.
1432     uuid = dependency('ossp-uuid', 'uuid', required: false)
1433     uuidfunc = 'uuid_export'
1434     uuidheader = 'uuid.h'
1436     # Hardcoded lookup for ossp-uuid. This is necessary as ossp-uuid on
1437     # windows installs neither a pkg-config nor a cmake dependency
1438     # information. Nor is there another supported uuid implementation
1439     # available on windows.
1440     if not uuid.found()
1441       uuid = cc.find_library('ossp-uuid',
1442         required: false, dirs: test_lib_d,
1443         has_headers: uuidheader, header_include_directories: postgres_inc)
1444     endif
1445     if not uuid.found()
1446       uuid = cc.find_library('uuid',
1447         required: true, dirs: test_lib_d,
1448         has_headers: uuidheader, header_include_directories: postgres_inc)
1449     endif
1450   else
1451     error('unknown uuid build option value: @0@'.format(uuidopt))
1452   endif
1454   if not cc.has_header_symbol(uuidheader, uuidfunc,
1455                               args: test_c_args,
1456                               include_directories: postgres_inc,
1457                               dependencies: uuid)
1458     error('uuid library @0@ missing required function @1@'.format(uuidopt, uuidfunc))
1459   endif
1460   cdata.set('HAVE_@0@'.format(uuidheader.underscorify().to_upper()), 1)
1462   cdata.set('HAVE_UUID_@0@'.format(uuidname), 1,
1463            description: 'Define to 1 if you have @0@ UUID support.'.format(uuidname))
1464 else
1465   uuid = not_found_dep
1466 endif
1470 ###############################################################
1471 # Library: zlib
1472 ###############################################################
1474 zlibopt = get_option('zlib')
1475 zlib = not_found_dep
1476 if not zlibopt.disabled()
1477   zlib_t = dependency('zlib', required: zlibopt)
1479   if zlib_t.type_name() == 'internal'
1480     # if fallback was used, we don't need to test if headers are present (they
1481     # aren't built yet, so we can't test)
1482     zlib = zlib_t
1483   elif not zlib_t.found()
1484     warning('did not find zlib')
1485   elif not cc.has_header('zlib.h',
1486       args: test_c_args, include_directories: postgres_inc,
1487       dependencies: [zlib_t], required: zlibopt)
1488     warning('zlib header not found')
1489   else
1490     zlib = zlib_t
1491   endif
1493   if zlib.found()
1494     cdata.set('HAVE_LIBZ', 1)
1495   endif
1496 endif
1500 ###############################################################
1501 # Library: tap test dependencies
1502 ###############################################################
1504 # Check whether tap tests are enabled or not
1505 tap_tests_enabled = false
1506 tapopt = get_option('tap_tests')
1507 if not tapopt.disabled()
1508   # Checking for perl modules for tap tests
1509   perl_ipc_run_check = run_command(perl, 'config/check_modules.pl', check: false)
1510   if perl_ipc_run_check.returncode() != 0
1511     message(perl_ipc_run_check.stderr().strip())
1512     if tapopt.enabled()
1513       error('Additional Perl modules are required to run TAP tests.')
1514     else
1515       warning('Additional Perl modules are required to run TAP tests.')
1516     endif
1517   else
1518     tap_tests_enabled = true
1519   endif
1520 endif
1524 ###############################################################
1525 # Library: zstd
1526 ###############################################################
1528 zstdopt = get_option('zstd')
1529 if not zstdopt.disabled()
1530   zstd = dependency('libzstd', required: false, version: '>=1.4.0')
1531   # Unfortunately the dependency is named differently with cmake
1532   if not zstd.found() # combine with above once meson 0.60.0 is required
1533     zstd = dependency('zstd', required: zstdopt, version: '>=1.4.0',
1534                       method: 'cmake', modules: ['zstd::libzstd_shared'])
1535   endif
1537   if zstd.found()
1538     cdata.set('USE_ZSTD', 1)
1539     cdata.set('HAVE_LIBZSTD', 1)
1540   endif
1542 else
1543   zstd = not_found_dep
1544 endif
1548 ###############################################################
1549 # Compiler tests
1550 ###############################################################
1552 # Do we need -std=c99 to compile C99 code? We don't want to add -std=c99
1553 # unnecessarily, because we optionally rely on newer features.
1554 c99_test = '''
1555 #include <stdbool.h>
1556 #include <complex.h>
1557 #include <tgmath.h>
1558 #include <inttypes.h>
1560 struct named_init_test {
1561   int a;
1562   int b;
1565 extern void structfunc(struct named_init_test);
1567 int main(int argc, char **argv)
1569   struct named_init_test nit = {
1570     .a = 3,
1571     .b = 5,
1572   };
1574   for (int loop_var = 0; loop_var < 3; loop_var++)
1575   {
1576     nit.a += nit.b;
1577   }
1579   structfunc((struct named_init_test){1, 0});
1581   return nit.a != 0;
1585 if not cc.compiles(c99_test, name: 'c99', args: test_c_args)
1586   if cc.compiles(c99_test, name: 'c99 with -std=c99',
1587         args: test_c_args + ['-std=c99'])
1588     test_c_args += '-std=c99'
1589     cflags += '-std=c99'
1590   else
1591     error('C compiler does not support C99')
1592   endif
1593 endif
1595 sizeof_long = cc.sizeof('long', args: test_c_args)
1596 cdata.set('SIZEOF_LONG', sizeof_long)
1597 if sizeof_long == 8
1598   cdata.set('HAVE_LONG_INT_64', 1)
1599   pg_int64_type = 'long int'
1600   cdata.set_quoted('INT64_MODIFIER', 'l')
1601 elif sizeof_long == 4 and cc.sizeof('long long', args: test_c_args) == 8
1602   cdata.set('HAVE_LONG_LONG_INT_64', 1)
1603   pg_int64_type = 'long long int'
1604   cdata.set_quoted('INT64_MODIFIER', 'll')
1605 else
1606   error('do not know how to get a 64bit int')
1607 endif
1608 cdata.set('PG_INT64_TYPE', pg_int64_type)
1610 if host_machine.endian() == 'big'
1611   cdata.set('WORDS_BIGENDIAN', 1)
1612 endif
1614 # Determine memory alignment requirements for the basic C data types.
1616 alignof_types = ['short', 'int', 'long', 'double']
1617 foreach t : alignof_types
1618   align = cc.alignment(t, args: test_c_args)
1619   cdata.set('ALIGNOF_@0@'.format(t.to_upper()), align)
1620 endforeach
1622 # Compute maximum alignment of any basic type.
1624 # We require 'double' to have the strictest alignment among the basic types,
1625 # because otherwise the C ABI might impose 8-byte alignment on some of the
1626 # other C types that correspond to TYPALIGN_DOUBLE SQL types.  That could
1627 # cause a mismatch between the tuple layout and the C struct layout of a
1628 # catalog tuple.  We used to carefully order catalog columns such that any
1629 # fixed-width, attalign=4 columns were at offsets divisible by 8 regardless
1630 # of MAXIMUM_ALIGNOF to avoid that, but we no longer support any platforms
1631 # where TYPALIGN_DOUBLE != MAXIMUM_ALIGNOF.
1633 # We assume without checking that int64's alignment is at least as strong
1634 # as long, char, short, or int.  Note that we intentionally do not consider
1635 # any types wider than 64 bits, as allowing MAXIMUM_ALIGNOF to exceed 8
1636 # would be too much of a penalty for disk and memory space.
1637 alignof_double = cdata.get('ALIGNOF_DOUBLE')
1638 if cc.alignment(pg_int64_type, args: test_c_args) > alignof_double
1639   error('alignment of int64 is greater than the alignment of double')
1640 endif
1641 cdata.set('MAXIMUM_ALIGNOF', alignof_double)
1643 cdata.set('SIZEOF_VOID_P', cc.sizeof('void *', args: test_c_args))
1644 cdata.set('SIZEOF_SIZE_T', cc.sizeof('size_t', args: test_c_args))
1647 # Check if __int128 is a working 128 bit integer type, and if so
1648 # define PG_INT128_TYPE to that typename.
1650 # This currently only detects a GCC/clang extension, but support for other
1651 # environments may be added in the future.
1653 # For the moment we only test for support for 128bit math; support for
1654 # 128bit literals and snprintf is not required.
1655 if cc.links('''
1656   /*
1657    * We don't actually run this test, just link it to verify that any support
1658    * functions needed for __int128 are present.
1659    *
1660    * These are globals to discourage the compiler from folding all the
1661    * arithmetic tests down to compile-time constants.  We do not have
1662    * convenient support for 128bit literals at this point...
1663    */
1664   __int128 a = 48828125;
1665   __int128 b = 97656250;
1667   int main(void)
1668   {
1669       __int128 c,d;
1670       a = (a << 12) + 1; /* 200000000001 */
1671       b = (b << 12) + 5; /* 400000000005 */
1672       /* try the most relevant arithmetic ops */
1673       c = a * b;
1674       d = (c + b) / b;
1675       /* must use the results, else compiler may optimize arithmetic away */
1676       return d != a+1;
1677   }''',
1678   name: '__int128',
1679   args: test_c_args)
1681   buggy_int128 = false
1683   # Use of non-default alignment with __int128 tickles bugs in some compilers.
1684   # If not cross-compiling, we can test for bugs and disable use of __int128
1685   # with buggy compilers.  If cross-compiling, hope for the best.
1686   # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83925
1687   if not meson.is_cross_build()
1688     r = cc.run('''
1689     /* This must match the corresponding code in c.h: */
1690     #if defined(__GNUC__) || defined(__SUNPRO_C)
1691     #define pg_attribute_aligned(a) __attribute__((aligned(a)))
1692     #elif defined(_MSC_VER)
1693     #define pg_attribute_aligned(a) __declspec(align(a))
1694     #endif
1695     typedef __int128 int128a
1696     #if defined(pg_attribute_aligned)
1697     pg_attribute_aligned(8)
1698     #endif
1699     ;
1701     int128a holder;
1702     void pass_by_val(void *buffer, int128a par) { holder = par; }
1704     int main(void)
1705     {
1706         long int i64 = 97656225L << 12;
1707         int128a q;
1708         pass_by_val(main, (int128a) i64);
1709         q = (int128a) i64;
1710         return q != holder;
1711     }''',
1712     name: '__int128 alignment bug',
1713     args: test_c_args)
1714     assert(r.compiled())
1715     if r.returncode() != 0
1716       buggy_int128 = true
1717       message('__int128 support present but buggy and thus disabled')
1718     endif
1719   endif
1721   if not buggy_int128
1722     cdata.set('PG_INT128_TYPE', '__int128')
1723     cdata.set('ALIGNOF_PG_INT128_TYPE', cc.alignment('__int128', args: test_c_args))
1724   endif
1725 endif
1728 # Check if the C compiler knows computed gotos (gcc extension, also
1729 # available in at least clang).  If so, define HAVE_COMPUTED_GOTO.
1731 # Checking whether computed gotos are supported syntax-wise ought to
1732 # be enough, as the syntax is otherwise illegal.
1733 if cc.compiles('''
1734     static inline int foo(void)
1735     {
1736       void *labeladdrs[] = {&&my_label};
1737       goto *labeladdrs[0];
1738       my_label:
1739       return 1;
1740     }''',
1741     args: test_c_args)
1742   cdata.set('HAVE_COMPUTED_GOTO', 1)
1743 endif
1746 # Check if the C compiler understands _Static_assert(),
1747 # and define HAVE__STATIC_ASSERT if so.
1749 # We actually check the syntax ({ _Static_assert(...) }), because we need
1750 # gcc-style compound expressions to be able to wrap the thing into macros.
1751 if cc.compiles('''
1752     int main(int arg, char **argv)
1753     {
1754         ({ _Static_assert(1, "foo"); });
1755     }
1756     ''',
1757     args: test_c_args)
1758   cdata.set('HAVE__STATIC_ASSERT', 1)
1759 endif
1762 # We use <stdbool.h> if we have it and it declares type bool as having
1763 # size 1.  Otherwise, c.h will fall back to declaring bool as unsigned char.
1764 if cc.has_type('_Bool', args: test_c_args) \
1765     and cc.has_type('bool', prefix: '#include <stdbool.h>', args: test_c_args) \
1766     and cc.sizeof('bool', prefix: '#include <stdbool.h>', args: test_c_args) == 1
1767   cdata.set('HAVE__BOOL', 1)
1768   cdata.set('PG_USE_STDBOOL', 1)
1769 endif
1772 # Need to check a call with %m because netbsd supports gnu_printf but emits a
1773 # warning for each use of %m.
1774 printf_attributes = ['gnu_printf', '__syslog__', 'printf']
1775 testsrc = '''
1776 extern void emit_log(int ignore, const char *fmt,...) __attribute__((format(@0@, 2,3)));
1777 static void call_log(void)
1779     emit_log(0, "error: %s: %m", "foo");
1782 attrib_error_args = cc.get_supported_arguments('-Werror=format', '-Werror=ignored-attributes')
1783 foreach a : printf_attributes
1784   if cc.compiles(testsrc.format(a),
1785       args: test_c_args + attrib_error_args, name: 'format ' + a)
1786     cdata.set('PG_PRINTF_ATTRIBUTE', a)
1787     break
1788   endif
1789 endforeach
1792 if cc.has_function_attribute('visibility:default') and \
1793     cc.has_function_attribute('visibility:hidden')
1794   cdata.set('HAVE_VISIBILITY_ATTRIBUTE', 1)
1796   # Only newer versions of meson know not to apply gnu_symbol_visibility =
1797   # inlineshidden to C code as well... And either way, we want to put these
1798   # flags into exported files (pgxs, .pc files).
1799   cflags_mod += '-fvisibility=hidden'
1800   cxxflags_mod += ['-fvisibility=hidden', '-fvisibility-inlines-hidden']
1801   ldflags_mod += '-fvisibility=hidden'
1802 endif
1805 # Check if various builtins exist. Some builtins are tested separately,
1806 # because we want to test something more complicated than the generic case.
1807 builtins = [
1808   'bswap16',
1809   'bswap32',
1810   'bswap64',
1811   'clz',
1812   'ctz',
1813   'constant_p',
1814   'frame_address',
1815   'popcount',
1816   'unreachable',
1819 foreach builtin : builtins
1820   fname = '__builtin_@0@'.format(builtin)
1821   if cc.has_function(fname, args: test_c_args)
1822     cdata.set('HAVE@0@'.format(fname.to_upper()), 1)
1823   endif
1824 endforeach
1827 # Check if the C compiler understands __builtin_types_compatible_p,
1828 # and define HAVE__BUILTIN_TYPES_COMPATIBLE_P if so.
1830 # We check usage with __typeof__, though it's unlikely any compiler would
1831 # have the former and not the latter.
1832 if cc.compiles('''
1833     static int x;
1834     static int y[__builtin_types_compatible_p(__typeof__(x), int)];
1835     ''',
1836     name: '__builtin_types_compatible_p',
1837     args: test_c_args)
1838   cdata.set('HAVE__BUILTIN_TYPES_COMPATIBLE_P', 1)
1839 endif
1842 # Check if the C compiler understands __builtin_$op_overflow(),
1843 # and define HAVE__BUILTIN_OP_OVERFLOW if so.
1845 # Check for the most complicated case, 64 bit multiplication, as a
1846 # proxy for all of the operations.  To detect the case where the compiler
1847 # knows the function but library support is missing, we must link not just
1848 # compile, and store the results in global variables so the compiler doesn't
1849 # optimize away the call.
1850 if cc.links('''
1851     INT64 a = 1;
1852     INT64 b = 1;
1853     INT64 result;
1855     int main(void)
1856     {
1857         return __builtin_mul_overflow(a, b, &result);
1858     }''',
1859     name: '__builtin_mul_overflow',
1860     args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))],
1861     )
1862   cdata.set('HAVE__BUILTIN_OP_OVERFLOW', 1)
1863 endif
1866 # XXX: The configure.ac check for __cpuid() is broken, we don't copy that
1867 # here. To prevent problems due to two detection methods working, stop
1868 # checking after one.
1869 if cc.links('''
1870     #include <cpuid.h>
1871     int main(int arg, char **argv)
1872     {
1873         unsigned int exx[4] = {0, 0, 0, 0};
1874         __get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
1875     }
1876     ''', name: '__get_cpuid',
1877     args: test_c_args)
1878   cdata.set('HAVE__GET_CPUID', 1)
1879 elif cc.links('''
1880     #include <intrin.h>
1881     int main(int arg, char **argv)
1882     {
1883         unsigned int exx[4] = {0, 0, 0, 0};
1884         __cpuid(exx, 1);
1885     }
1886     ''', name: '__cpuid',
1887     args: test_c_args)
1888   cdata.set('HAVE__CPUID', 1)
1889 endif
1892 # Check for __get_cpuid_count() and __cpuidex() in a similar fashion.
1893 if cc.links('''
1894     #include <cpuid.h>
1895     int main(int arg, char **argv)
1896     {
1897         unsigned int exx[4] = {0, 0, 0, 0};
1898         __get_cpuid_count(7, 0, &exx[0], &exx[1], &exx[2], &exx[3]);
1899     }
1900     ''', name: '__get_cpuid_count',
1901     args: test_c_args)
1902   cdata.set('HAVE__GET_CPUID_COUNT', 1)
1903 elif cc.links('''
1904     #include <intrin.h>
1905     int main(int arg, char **argv)
1906     {
1907         unsigned int exx[4] = {0, 0, 0, 0};
1908         __cpuidex(exx, 7, 0);
1909     }
1910     ''', name: '__cpuidex',
1911     args: test_c_args)
1912   cdata.set('HAVE__CPUIDEX', 1)
1913 endif
1916 # Defend against clang being used on x86-32 without SSE2 enabled.  As current
1917 # versions of clang do not understand -fexcess-precision=standard, the use of
1918 # x87 floating point operations leads to problems like isinf possibly returning
1919 # false for a value that is infinite when converted from the 80bit register to
1920 # the 8byte memory representation.
1922 # Only perform the test if the compiler doesn't understand
1923 # -fexcess-precision=standard, that way a potentially fixed compiler will work
1924 # automatically.
1925 if '-fexcess-precision=standard' not in cflags
1926   if not cc.compiles('''
1927 #if defined(__clang__) && defined(__i386__) && !defined(__SSE2_MATH__)
1928 choke me
1929 #endif''',
1930       name: '', args: test_c_args)
1931     error('Compiling PostgreSQL with clang, on 32bit x86, requires SSE2 support. Use -msse2 or use gcc.')
1932   endif
1933 endif
1937 ###############################################################
1938 # Compiler flags
1939 ###############################################################
1941 common_functional_flags = [
1942   # Disable strict-aliasing rules; needed for gcc 3.3+
1943   '-fno-strict-aliasing',
1944   # Disable optimizations that assume no overflow; needed for gcc 4.3+
1945   '-fwrapv',
1946   '-fexcess-precision=standard',
1949 cflags += cc.get_supported_arguments(common_functional_flags)
1950 if llvm.found()
1951   cxxflags += cpp.get_supported_arguments(common_functional_flags)
1952 endif
1954 vectorize_cflags = cc.get_supported_arguments(['-ftree-vectorize'])
1955 unroll_loops_cflags = cc.get_supported_arguments(['-funroll-loops'])
1957 common_warning_flags = [
1958   '-Wmissing-prototypes',
1959   '-Wpointer-arith',
1960   # Really don't want VLAs to be used in our dialect of C
1961   '-Werror=vla',
1962   # On macOS, complain about usage of symbols newer than the deployment target
1963   '-Werror=unguarded-availability-new',
1964   '-Wendif-labels',
1965   '-Wmissing-format-attribute',
1966   '-Wimplicit-fallthrough=3',
1967   '-Wcast-function-type',
1968   '-Wshadow=compatible-local',
1969   # This was included in -Wall/-Wformat in older GCC versions
1970   '-Wformat-security',
1973 cflags_warn += cc.get_supported_arguments(common_warning_flags)
1974 if llvm.found()
1975   cxxflags_warn += cpp.get_supported_arguments(common_warning_flags)
1976 endif
1978 # A few places with imported code get a pass on -Wdeclaration-after-statement, remember
1979 # the result for them
1980 cflags_no_decl_after_statement = []
1981 if cc.has_argument('-Wdeclaration-after-statement')
1982   cflags_warn += '-Wdeclaration-after-statement'
1983   cflags_no_decl_after_statement += '-Wno-declaration-after-statement'
1984 endif
1986 # Some code is not clean for -Wmissing-variable-declarations, so we
1987 # make the "no" option available.  Also, while clang supports this
1988 # option for C++, gcc does not, so for consistency, leave it off for
1989 # C++.
1990 cflags_no_missing_var_decls = []
1991 if cc.has_argument('-Wmissing-variable-declarations')
1992   cflags_warn += '-Wmissing-variable-declarations'
1993   cflags_no_missing_var_decls += '-Wno-missing-variable-declarations'
1994 endif
1997 # The following tests want to suppress various unhelpful warnings by adding
1998 # -Wno-foo switches.  But gcc won't complain about unrecognized -Wno-foo
1999 # switches, so we have to test for the positive form and if that works,
2000 # add the negative form.
2002 negative_warning_flags = [
2003   # Suppress clang's unhelpful unused-command-line-argument warnings.
2004   'unused-command-line-argument',
2006   # Remove clang 12+'s compound-token-split-by-macro, as this causes a lot
2007   # of warnings when building plperl because of usages in the Perl headers.
2008   'compound-token-split-by-macro',
2010   # Similarly disable useless truncation warnings from gcc 8+
2011   'format-truncation',
2012   'stringop-truncation',
2014   # Suppress clang 16's strict warnings about function casts
2015   'cast-function-type-strict',
2017   # To make warning_level=2 / -Wextra work, we'd need at least the following
2018   # 'clobbered',
2019   # 'missing-field-initializers',
2020   # 'sign-compare',
2021   # 'unused-parameter',
2024 foreach w : negative_warning_flags
2025   if cc.has_argument('-W' + w)
2026     cflags_warn += '-Wno-' + w
2027   endif
2028   if llvm.found() and cpp.has_argument('-W' + w)
2029     cxxflags_warn += '-Wno-' + w
2030   endif
2031 endforeach
2034 if cc.get_id() == 'msvc'
2035   cflags_warn += [
2036     '/wd4018', # signed/unsigned mismatch
2037     '/wd4244', # conversion from 'type1' to 'type2', possible loss of data
2038     '/wd4273', # inconsistent DLL linkage
2039     '/wd4101', # unreferenced local variable
2040     '/wd4102', # unreferenced label
2041     '/wd4090', # different 'modifier' qualifiers
2042     '/wd4267', # conversion from 'size_t' to 'type', possible loss of data
2043   ]
2045   cppflags += [
2046     '/DWIN32',
2047     '/DWINDOWS',
2048     '/D__WINDOWS__',
2049     '/D__WIN32__',
2050     '/D_CRT_SECURE_NO_DEPRECATE',
2051     '/D_CRT_NONSTDC_NO_DEPRECATE',
2052   ]
2054   # We never need export libraries. As link.exe reports their creation, they
2055   # are unnecessarily noisy. Similarly, we don't need import library for
2056   # modules, we only import them dynamically, and they're also noisy.
2057   ldflags += '/NOEXP'
2058   ldflags_mod += '/NOIMPLIB'
2059 endif
2062 # Compute flags that are built into Meson.  We need these to
2063 # substitute into Makefile.global and for pg_config.  We only compute
2064 # the flags for Unix-style compilers, since that's the only style that
2065 # would use Makefile.global or pg_config.
2067 # We don't use get_option('warning_level') here, because the other
2068 # warning levels are not useful with PostgreSQL source code.
2069 common_builtin_flags = ['-Wall']
2071 if get_option('debug')
2072   common_builtin_flags += ['-g']
2073 endif
2075 optimization = get_option('optimization')
2076 if optimization == '0'
2077   common_builtin_flags += ['-O0']
2078 elif optimization == '1'
2079   common_builtin_flags += ['-O1']
2080 elif optimization == '2'
2081   common_builtin_flags += ['-O2']
2082 elif optimization == '3'
2083   common_builtin_flags += ['-O3']
2084 elif optimization == 's'
2085   common_builtin_flags += ['-Os']
2086 endif
2088 cflags_builtin = cc.get_supported_arguments(common_builtin_flags)
2089 if llvm.found()
2090   cxxflags_builtin = cpp.get_supported_arguments(common_builtin_flags)
2091 endif
2095 ###############################################################
2096 # Atomics
2097 ###############################################################
2099 atomic_checks = [
2100   {'name': 'HAVE_GCC__SYNC_CHAR_TAS',
2101    'desc': '__sync_lock_test_and_set(char)',
2102    'test': '''
2103 char lock = 0;
2104 __sync_lock_test_and_set(&lock, 1);
2105 __sync_lock_release(&lock);'''},
2107   {'name': 'HAVE_GCC__SYNC_INT32_TAS',
2108    'desc': '__sync_lock_test_and_set(int32)',
2109    'test': '''
2110 int lock = 0;
2111 __sync_lock_test_and_set(&lock, 1);
2112 __sync_lock_release(&lock);'''},
2114   {'name': 'HAVE_GCC__SYNC_INT32_CAS',
2115    'desc': '__sync_val_compare_and_swap(int32)',
2116    'test': '''
2117 int val = 0;
2118 __sync_val_compare_and_swap(&val, 0, 37);'''},
2120   {'name': 'HAVE_GCC__SYNC_INT64_CAS',
2121    'desc': '__sync_val_compare_and_swap(int64)',
2122    'test': '''
2123 INT64 val = 0;
2124 __sync_val_compare_and_swap(&val, 0, 37);'''},
2126   {'name': 'HAVE_GCC__ATOMIC_INT32_CAS',
2127    'desc': ' __atomic_compare_exchange_n(int32)',
2128    'test': '''
2129 int val = 0;
2130 int expect = 0;
2131 __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);'''},
2133   {'name': 'HAVE_GCC__ATOMIC_INT64_CAS',
2134    'desc': ' __atomic_compare_exchange_n(int64)',
2135    'test': '''
2136 INT64 val = 0;
2137 INT64 expect = 0;
2138 __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);'''},
2141 foreach check : atomic_checks
2142   test = '''
2143 int main(void)
2146 }'''.format(check['test'])
2148   cdata.set(check['name'],
2149     cc.links(test,
2150       name: check['desc'],
2151       args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))]) ? 1 : false
2152   )
2153 endforeach
2156 ###############################################################
2157 # Check for the availability of XSAVE intrinsics.
2158 ###############################################################
2160 cflags_xsave = []
2161 if host_cpu == 'x86' or host_cpu == 'x86_64'
2163   prog = '''
2164 #include <immintrin.h>
2166 int main(void)
2168     return _xgetbv(0) & 0xe0;
2172   if cc.links(prog, name: 'XSAVE intrinsics without -mxsave',
2173         args: test_c_args)
2174     cdata.set('HAVE_XSAVE_INTRINSICS', 1)
2175   elif cc.links(prog, name: 'XSAVE intrinsics with -mxsave',
2176         args: test_c_args + ['-mxsave'])
2177     cdata.set('HAVE_XSAVE_INTRINSICS', 1)
2178     cflags_xsave += '-mxsave'
2179   endif
2181 endif
2184 ###############################################################
2185 # Check for the availability of AVX-512 popcount intrinsics.
2186 ###############################################################
2188 cflags_popcnt = []
2189 if host_cpu == 'x86_64'
2191   prog = '''
2192 #include <immintrin.h>
2194 int main(void)
2196     const char buf[sizeof(__m512i)];
2197     INT64 popcnt = 0;
2198     __m512i accum = _mm512_setzero_si512();
2199     const __m512i val = _mm512_maskz_loadu_epi8((__mmask64) 0xf0f0f0f0f0f0f0f0, (const __m512i *) buf);
2200     const __m512i cnt = _mm512_popcnt_epi64(val);
2201     accum = _mm512_add_epi64(accum, cnt);
2202     popcnt = _mm512_reduce_add_epi64(accum);
2203     /* return computed value, to prevent the above being optimized away */
2204     return popcnt == 0;
2208   if cc.links(prog, name: 'AVX-512 popcount without -mavx512vpopcntdq -mavx512bw',
2209         args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))])
2210     cdata.set('USE_AVX512_POPCNT_WITH_RUNTIME_CHECK', 1)
2211   elif cc.links(prog, name: 'AVX-512 popcount with -mavx512vpopcntdq -mavx512bw',
2212         args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))] + ['-mavx512vpopcntdq'] + ['-mavx512bw'])
2213     cdata.set('USE_AVX512_POPCNT_WITH_RUNTIME_CHECK', 1)
2214     cflags_popcnt += ['-mavx512vpopcntdq'] + ['-mavx512bw']
2215   endif
2217 endif
2220 ###############################################################
2221 # Select CRC-32C implementation.
2223 # If we are targeting a processor that has Intel SSE 4.2 instructions, we can
2224 # use the special CRC instructions for calculating CRC-32C. If we're not
2225 # targeting such a processor, but we can nevertheless produce code that uses
2226 # the SSE intrinsics, perhaps with some extra CFLAGS, compile both
2227 # implementations and select which one to use at runtime, depending on whether
2228 # SSE 4.2 is supported by the processor we're running on.
2230 # Similarly, if we are targeting an ARM processor that has the CRC
2231 # instructions that are part of the ARMv8 CRC Extension, use them. And if
2232 # we're not targeting such a processor, but can nevertheless produce code that
2233 # uses the CRC instructions, compile both, and select at runtime.
2234 ###############################################################
2236 have_optimized_crc = false
2237 cflags_crc = []
2238 if host_cpu == 'x86' or host_cpu == 'x86_64'
2240   if cc.get_id() == 'msvc'
2241     cdata.set('USE_SSE42_CRC32C', false)
2242     cdata.set('USE_SSE42_CRC32C_WITH_RUNTIME_CHECK', 1)
2243     have_optimized_crc = true
2244   else
2246     prog = '''
2247 #include <nmmintrin.h>
2249 int main(void)
2251     unsigned int crc = 0;
2252     crc = _mm_crc32_u8(crc, 0);
2253     crc = _mm_crc32_u32(crc, 0);
2254     /* return computed value, to prevent the above being optimized away */
2255     return crc == 0;
2259     if cc.links(prog, name: '_mm_crc32_u8 and _mm_crc32_u32 without -msse4.2',
2260           args: test_c_args)
2261       # Use Intel SSE 4.2 unconditionally.
2262       cdata.set('USE_SSE42_CRC32C', 1)
2263       have_optimized_crc = true
2264     elif cc.links(prog, name: '_mm_crc32_u8 and _mm_crc32_u32 with -msse4.2',
2265           args: test_c_args + ['-msse4.2'])
2266       # Use Intel SSE 4.2, with runtime check. The CPUID instruction is needed for
2267       # the runtime check.
2268       cflags_crc += '-msse4.2'
2269       cdata.set('USE_SSE42_CRC32C', false)
2270       cdata.set('USE_SSE42_CRC32C_WITH_RUNTIME_CHECK', 1)
2271       have_optimized_crc = true
2272     endif
2274   endif
2276 elif host_cpu == 'arm' or host_cpu == 'aarch64'
2278   prog = '''
2279 #include <arm_acle.h>
2281 int main(void)
2283     unsigned int crc = 0;
2284     crc = __crc32cb(crc, 0);
2285     crc = __crc32ch(crc, 0);
2286     crc = __crc32cw(crc, 0);
2287     crc = __crc32cd(crc, 0);
2289     /* return computed value, to prevent the above being optimized away */
2290     return crc == 0;
2294   if cc.links(prog, name: '__crc32cb, __crc32ch, __crc32cw, and __crc32cd without -march=armv8-a+crc',
2295       args: test_c_args)
2296     # Use ARM CRC Extension unconditionally
2297     cdata.set('USE_ARMV8_CRC32C', 1)
2298     have_optimized_crc = true
2299   elif cc.links(prog, name: '__crc32cb, __crc32ch, __crc32cw, and __crc32cd with -march=armv8-a+crc',
2300       args: test_c_args + ['-march=armv8-a+crc'])
2301     # Use ARM CRC Extension, with runtime check
2302     cflags_crc += '-march=armv8-a+crc'
2303     cdata.set('USE_ARMV8_CRC32C', false)
2304     cdata.set('USE_ARMV8_CRC32C_WITH_RUNTIME_CHECK', 1)
2305     have_optimized_crc = true
2306   endif
2308 elif host_cpu == 'loongarch64'
2310   prog = '''
2311 int main(void)
2313     unsigned int crc = 0;
2314     crc = __builtin_loongarch_crcc_w_b_w(0, crc);
2315     crc = __builtin_loongarch_crcc_w_h_w(0, crc);
2316     crc = __builtin_loongarch_crcc_w_w_w(0, crc);
2317     crc = __builtin_loongarch_crcc_w_d_w(0, crc);
2319     /* return computed value, to prevent the above being optimized away */
2320     return crc == 0;
2324   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',
2325       args: test_c_args)
2326     # Use LoongArch CRC instruction unconditionally
2327     cdata.set('USE_LOONGARCH_CRC32C', 1)
2328     have_optimized_crc = true
2329   endif
2331 endif
2333 if not have_optimized_crc
2334   # fall back to slicing-by-8 algorithm, which doesn't require any special CPU
2335   # support.
2336   cdata.set('USE_SLICING_BY_8_CRC32C', 1)
2337 endif
2341 ###############################################################
2342 # Other CPU specific stuff
2343 ###############################################################
2345 if host_cpu == 'x86_64'
2347   if cc.compiles('''
2348       void main(void)
2349       {
2350           long long x = 1; long long r;
2351           __asm__ __volatile__ (" popcntq %1,%0\n" : "=q"(r) : "rm"(x));
2352       }''',
2353       name: '@0@: popcntq instruction'.format(host_cpu),
2354       args: test_c_args)
2355     cdata.set('HAVE_X86_64_POPCNTQ', 1)
2356   endif
2358 elif host_cpu == 'ppc' or host_cpu == 'ppc64'
2359   # Check if compiler accepts "i"(x) when __builtin_constant_p(x).
2360   if cdata.has('HAVE__BUILTIN_CONSTANT_P')
2361     if cc.compiles('''
2362       static inline int
2363       addi(int ra, int si)
2364       {
2365           int res = 0;
2366           if (__builtin_constant_p(si))
2367               __asm__ __volatile__(
2368                   " addi %0,%1,%2\n" : "=r"(res) : "b"(ra), "i"(si));
2369           return res;
2370       }
2371       int test_adds(int x) { return addi(3, x) + addi(x, 5); }
2372       ''',
2373       args: test_c_args)
2374       cdata.set('HAVE_I_CONSTRAINT__BUILTIN_CONSTANT_P', 1)
2375     endif
2376   endif
2377 endif
2381 ###############################################################
2382 # Library / OS tests
2383 ###############################################################
2385 # XXX: Might be worth conditioning some checks on the OS, to avoid doing
2386 # unnecessary checks over and over, particularly on windows.
2387 header_checks = [
2388   'atomic.h',
2389   'copyfile.h',
2390   'crtdefs.h',
2391   'execinfo.h',
2392   'getopt.h',
2393   'ifaddrs.h',
2394   'mbarrier.h',
2395   'stdbool.h',
2396   'strings.h',
2397   'sys/epoll.h',
2398   'sys/event.h',
2399   'sys/personality.h',
2400   'sys/prctl.h',
2401   'sys/procctl.h',
2402   'sys/signalfd.h',
2403   'sys/ucred.h',
2404   'termios.h',
2405   'ucred.h',
2408 foreach header : header_checks
2409   varname = 'HAVE_' + header.underscorify().to_upper()
2411   # Emulate autoconf behaviour of not-found->undef, found->1
2412   found = cc.has_header(header,
2413     include_directories: postgres_inc, args: test_c_args)
2414   cdata.set(varname, found ? 1 : false,
2415             description: 'Define to 1 if you have the <@0@> header file.'.format(header))
2416 endforeach
2419 decl_checks = [
2420   ['F_FULLFSYNC', 'fcntl.h'],
2421   ['fdatasync', 'unistd.h'],
2422   ['posix_fadvise', 'fcntl.h'],
2423   ['strlcat', 'string.h'],
2424   ['strlcpy', 'string.h'],
2425   ['strnlen', 'string.h'],
2426   ['strsep',  'string.h'],
2429 # Need to check for function declarations for these functions, because
2430 # checking for library symbols wouldn't handle deployment target
2431 # restrictions on macOS
2432 decl_checks += [
2433   ['preadv', 'sys/uio.h'],
2434   ['pwritev', 'sys/uio.h'],
2437 # Check presence of some optional LLVM functions.
2438 if llvm.found()
2439   decl_checks += [
2440     ['LLVMCreateGDBRegistrationListener', 'llvm-c/ExecutionEngine.h'],
2441     ['LLVMCreatePerfJITEventListener', 'llvm-c/ExecutionEngine.h'],
2442   ]
2443 endif
2445 foreach c : decl_checks
2446   func = c.get(0)
2447   header = c.get(1)
2448   args = c.get(2, {})
2449   varname = 'HAVE_DECL_' + func.underscorify().to_upper()
2451   found = cc.has_header_symbol(header, func,
2452     args: test_c_args, include_directories: postgres_inc,
2453     kwargs: args)
2454   cdata.set10(varname, found, description:
2455 '''Define to 1 if you have the declaration of `@0@', and to 0 if you
2456    don't.'''.format(func))
2457 endforeach
2460 if cc.has_type('struct option',
2461     args: test_c_args, include_directories: postgres_inc,
2462     prefix: '@0@'.format(cdata.get('HAVE_GETOPT_H')) == '1' ? '#include <getopt.h>' : '')
2463   cdata.set('HAVE_STRUCT_OPTION', 1)
2464 endif
2467 foreach c : ['opterr', 'optreset']
2468   varname = 'HAVE_INT_' + c.underscorify().to_upper()
2470   if cc.links('''
2471 #include <unistd.h>
2472 int main(void)
2474     extern int @0@;
2475     @0@ = 1;
2477 '''.format(c), name: c, args: test_c_args)
2478     cdata.set(varname, 1)
2479   else
2480     cdata.set(varname, false)
2481   endif
2482 endforeach
2484 if cc.has_type('socklen_t',
2485     args: test_c_args, include_directories: postgres_inc,
2486     prefix: '''
2487 #include <sys/socket.h>''')
2488   cdata.set('HAVE_SOCKLEN_T', 1)
2489 endif
2491 if cc.has_member('struct sockaddr', 'sa_len',
2492     args: test_c_args, include_directories: postgres_inc,
2493     prefix: '''
2494 #include <sys/types.h>
2495 #include <sys/socket.h>''')
2496   cdata.set('HAVE_STRUCT_SOCKADDR_SA_LEN', 1)
2497 endif
2499 if cc.has_member('struct tm', 'tm_zone',
2500     args: test_c_args, include_directories: postgres_inc,
2501     prefix: '''
2502 #include <sys/types.h>
2503 #include <time.h>
2504 ''')
2505   cdata.set('HAVE_STRUCT_TM_TM_ZONE', 1)
2506 endif
2508 if cc.compiles('''
2509 #include <time.h>
2510 extern int foo(void);
2511 int foo(void)
2513     return timezone / 60;
2515 ''',
2516     name: 'global variable `timezone\' exists',
2517     args: test_c_args, include_directories: postgres_inc)
2518   cdata.set('HAVE_INT_TIMEZONE', 1)
2519 else
2520   cdata.set('HAVE_INT_TIMEZONE', false)
2521 endif
2523 if cc.has_type('union semun',
2524     args: test_c_args,
2525     include_directories: postgres_inc,
2526     prefix: '''
2527 #include <sys/types.h>
2528 #include <sys/ipc.h>
2529 #include <sys/sem.h>
2530 ''')
2531   cdata.set('HAVE_UNION_SEMUN', 1)
2532 endif
2534 if cc.compiles('''
2535 #include <string.h>
2536 int main(void)
2538   char buf[100];
2539   switch (strerror_r(1, buf, sizeof(buf)))
2540   { case 0: break; default: break; }
2541 }''',
2542     name: 'strerror_r',
2543     args: test_c_args, include_directories: postgres_inc)
2544   cdata.set('STRERROR_R_INT', 1)
2545 else
2546   cdata.set('STRERROR_R_INT', false)
2547 endif
2549 # Find the right header file for the locale_t type.  macOS needs xlocale.h;
2550 # standard is locale.h, but glibc <= 2.25 also had an xlocale.h file that
2551 # we should not use so we check the standard header first.  MSVC has a
2552 # replacement defined in src/include/port/win32_port.h.
2553 if not cc.has_type('locale_t', prefix: '#include <locale.h>') and \
2554    cc.has_type('locale_t', prefix: '#include <xlocale.h>')
2555   cdata.set('LOCALE_T_IN_XLOCALE', 1)
2556 endif
2558 # Check if the C compiler understands typeof or a variant.  Define
2559 # HAVE_TYPEOF if so, and define 'typeof' to the actual key word.
2560 foreach kw : ['typeof', '__typeof__', 'decltype']
2561   if cc.compiles('''
2562 int main(void)
2564     int x = 0;
2565     @0@(x) y;
2566     y = x;
2567     return y;
2569 '''.format(kw),
2570     name: 'typeof()',
2571     args: test_c_args, include_directories: postgres_inc)
2573     cdata.set('HAVE_TYPEOF', 1)
2574     if kw != 'typeof'
2575       cdata.set('typeof', kw)
2576     endif
2578     break
2579   endif
2580 endforeach
2583 # Try to find a declaration for wcstombs_l().  It might be in stdlib.h
2584 # (following the POSIX requirement for wcstombs()), or in locale.h, or in
2585 # xlocale.h.  If it's in the latter, define WCSTOMBS_L_IN_XLOCALE.
2586 wcstombs_l_test = '''
2587 #include <stdlib.h>
2588 #include <locale.h>
2591 void main(void)
2593 #ifndef wcstombs_l
2594     (void) wcstombs_l;
2595 #endif
2598 if (not cc.compiles(wcstombs_l_test.format(''),
2599       name: 'wcstombs_l') and
2600     cc.compiles(wcstombs_l_test.format('#include <xlocale.h>'),
2601       name: 'wcstombs_l in xlocale.h'))
2602     cdata.set('WCSTOMBS_L_IN_XLOCALE', 1)
2603 endif
2606 # MSVC doesn't cope well with defining restrict to __restrict, the spelling it
2607 # understands, because it conflicts with __declspec(restrict). Therefore we
2608 # define pg_restrict to the appropriate definition, which presumably won't
2609 # conflict.
2611 # We assume C99 support, so we don't need to make this conditional.
2612 cdata.set('pg_restrict', '__restrict')
2615 # Most libraries are included only if they demonstrably provide a function we
2616 # need, but libm is an exception: always include it, because there are too
2617 # many compilers that play cute optimization games that will break probes for
2618 # standard functions such as pow().
2619 os_deps += cc.find_library('m', required: false)
2621 rt_dep = cc.find_library('rt', required: false)
2623 dl_dep = cc.find_library('dl', required: false)
2625 util_dep = cc.find_library('util', required: false)
2627 getopt_dep = cc.find_library('getopt', required: false)
2628 gnugetopt_dep = cc.find_library('gnugetopt', required: false)
2629 # Check if we want to replace getopt/getopt_long even if provided by the system
2630 # - Mingw has adopted a GNU-centric interpretation of optind/optreset,
2631 #   so always use our version on Windows
2632 # - On OpenBSD and Solaris, getopt() doesn't do what we want for long options
2633 #   (i.e., allow '-' as a flag character), so use our version on those platforms
2634 # - We want to use system's getopt_long() only if the system provides struct
2635 #   option
2636 always_replace_getopt = host_system in ['windows', 'cygwin', 'openbsd', 'solaris']
2637 always_replace_getopt_long = host_system in ['windows', 'cygwin'] or not cdata.has('HAVE_STRUCT_OPTION')
2639 # Required on BSDs
2640 execinfo_dep = cc.find_library('execinfo', required: false)
2642 if host_system == 'cygwin'
2643   cygipc_dep = cc.find_library('cygipc', required: false)
2644 else
2645   cygipc_dep = not_found_dep
2646 endif
2648 if host_system == 'sunos'
2649   socket_dep = cc.find_library('socket', required: false)
2650 else
2651   socket_dep = not_found_dep
2652 endif
2654 # XXX: Might be worth conditioning some checks on the OS, to avoid doing
2655 # unnecessary checks over and over, particularly on windows.
2656 func_checks = [
2657   ['_configthreadlocale', {'skip': host_system != 'windows'}],
2658   ['backtrace_symbols', {'dependencies': [execinfo_dep]}],
2659   ['clock_gettime', {'dependencies': [rt_dep], 'define': false}],
2660   ['copyfile'],
2661   ['copy_file_range'],
2662   # gcc/clang's sanitizer helper library provides dlopen but not dlsym, thus
2663   # when enabling asan the dlopen check doesn't notice that -ldl is actually
2664   # required. Just checking for dlsym() ought to suffice.
2665   ['dlsym', {'dependencies': [dl_dep], 'define': false}],
2666   ['explicit_bzero'],
2667   ['getifaddrs'],
2668   ['getopt', {'dependencies': [getopt_dep, gnugetopt_dep], 'skip': always_replace_getopt}],
2669   ['getopt_long', {'dependencies': [getopt_dep, gnugetopt_dep], 'skip': always_replace_getopt_long}],
2670   ['getpeereid'],
2671   ['getpeerucred'],
2672   ['inet_aton'],
2673   ['inet_pton'],
2674   ['kqueue'],
2675   ['mbstowcs_l'],
2676   ['memset_s'],
2677   ['mkdtemp'],
2678   ['posix_fadvise'],
2679   ['posix_fallocate'],
2680   ['ppoll'],
2681   ['pthread_barrier_wait', {'dependencies': [thread_dep]}],
2682   ['pthread_is_threaded_np', {'dependencies': [thread_dep]}],
2683   ['sem_init', {'dependencies': [rt_dep, thread_dep], 'skip': sema_kind != 'unnamed_posix', 'define': false}],
2684   ['setproctitle', {'dependencies': [util_dep]}],
2685   ['setproctitle_fast'],
2686   ['shm_open', {'dependencies': [rt_dep], 'define': false}],
2687   ['shm_unlink', {'dependencies': [rt_dep], 'define': false}],
2688   ['shmget', {'dependencies': [cygipc_dep], 'define': false}],
2689   ['socket', {'dependencies': [socket_dep], 'define': false}],
2690   ['strchrnul'],
2691   ['strerror_r', {'dependencies': [thread_dep]}],
2692   ['strlcat'],
2693   ['strlcpy'],
2694   ['strnlen'],
2695   ['strsep'],
2696   ['strsignal'],
2697   ['sync_file_range'],
2698   ['syncfs'],
2699   ['uselocale'],
2700   ['wcstombs_l'],
2703 func_check_results = {}
2704 foreach c : func_checks
2705   func = c.get(0)
2706   kwargs = c.get(1, {})
2707   deps = kwargs.get('dependencies', [])
2709   if kwargs.get('skip', false)
2710     continue
2711   endif
2713   found = cc.has_function(func, args: test_c_args)
2715   if not found
2716     foreach dep : deps
2717       if not dep.found()
2718         continue
2719       endif
2720       found = cc.has_function(func, args: test_c_args,
2721                               dependencies: [dep])
2722       if found
2723         os_deps += dep
2724         break
2725       endif
2726     endforeach
2727   endif
2729   func_check_results += {func: found}
2731   if kwargs.get('define', true)
2732     # Emulate autoconf behaviour of not-found->undef, found->1
2733     cdata.set('HAVE_' + func.underscorify().to_upper(),
2734               found  ? 1 : false,
2735               description: 'Define to 1 if you have the `@0@\' function.'.format(func))
2736   endif
2737 endforeach
2740 if cc.has_function('syslog', args: test_c_args) and \
2741     cc.check_header('syslog.h', args: test_c_args)
2742   cdata.set('HAVE_SYSLOG', 1)
2743 endif
2746 # if prerequisites for unnamed posix semas aren't fulfilled, fall back to sysv
2747 # semaphores
2748 if sema_kind == 'unnamed_posix' and \
2749    not func_check_results.get('sem_init', false)
2750   sema_kind = 'sysv'
2751 endif
2753 cdata.set('USE_@0@_SHARED_MEMORY'.format(shmem_kind.to_upper()), 1)
2754 cdata.set('USE_@0@_SEMAPHORES'.format(sema_kind.to_upper()), 1)
2756 cdata.set('MEMSET_LOOP_LIMIT', memset_loop_limit)
2757 cdata.set_quoted('DLSUFFIX', dlsuffix)
2760 # built later than the rest of the version metadata, we need SIZEOF_VOID_P
2761 cdata.set_quoted('PG_VERSION_STR',
2762   'PostgreSQL @0@ on @1@-@2@, compiled by @3@-@4@, @5@-bit'.format(
2763     pg_version, host_machine.cpu_family(), host_system,
2764     cc.get_id(), cc.version(), cdata.get('SIZEOF_VOID_P') * 8,
2765   )
2769 ###############################################################
2770 # NLS / Gettext
2771 ###############################################################
2773 nlsopt = get_option('nls')
2774 libintl = not_found_dep
2776 if not nlsopt.disabled()
2777   # otherwise there'd be lots of
2778   # "Gettext not found, all translation (po) targets will be ignored."
2779   # warnings if not found.
2780   msgfmt = find_program('msgfmt', required: nlsopt, native: true)
2782   # meson 0.59 has this wrapped in dependency('intl')
2783   if (msgfmt.found() and
2784       cc.check_header('libintl.h', required: nlsopt,
2785         args: test_c_args, include_directories: postgres_inc))
2787     # in libc
2788     if cc.has_function('ngettext')
2789       libintl = declare_dependency()
2790     else
2791       libintl = cc.find_library('intl',
2792         has_headers: ['libintl.h'], required: nlsopt,
2793         header_include_directories: postgres_inc,
2794         dirs: test_lib_d)
2795     endif
2796   endif
2798   if libintl.found()
2799     i18n = import('i18n')
2800     cdata.set('ENABLE_NLS', 1)
2801   endif
2802 endif
2806 ###############################################################
2807 # Build
2808 ###############################################################
2810 # Set up compiler / linker arguments to be used everywhere, individual targets
2811 # can add further args directly, or indirectly via dependencies
2812 add_project_arguments(cflags, language: ['c'])
2813 add_project_arguments(cppflags, language: ['c'])
2814 add_project_arguments(cflags_warn, language: ['c'])
2815 add_project_arguments(cxxflags, language: ['cpp'])
2816 add_project_arguments(cppflags, language: ['cpp'])
2817 add_project_arguments(cxxflags_warn, language: ['cpp'])
2818 add_project_link_arguments(ldflags, language: ['c', 'cpp'])
2821 # Collect a number of lists of things while recursing through the source
2822 # tree. Later steps then can use those.
2824 # list of targets for various alias targets
2825 backend_targets = []
2826 bin_targets = []
2827 pl_targets = []
2828 contrib_targets = []
2829 testprep_targets = []
2830 nls_targets = []
2833 # Define the tests to distribute them to the correct test styles later
2834 test_deps = []
2835 tests = []
2838 # Default options for targets
2840 # First identify rpaths
2841 bin_install_rpaths = []
2842 lib_install_rpaths = []
2843 mod_install_rpaths = []
2846 # Don't add rpaths on darwin for now - as long as only absolute references to
2847 # libraries are needed, absolute LC_ID_DYLIB ensures libraries can be found in
2848 # their final destination.
2849 if host_system != 'darwin'
2850   # Add absolute path to libdir to rpath. This ensures installed binaries /
2851   # libraries find our libraries (mainly libpq).
2852   bin_install_rpaths += dir_prefix / dir_lib
2853   lib_install_rpaths += dir_prefix / dir_lib
2854   mod_install_rpaths += dir_prefix / dir_lib
2856   # Add extra_lib_dirs to rpath. This ensures we find libraries we depend on.
2857   #
2858   # Not needed on darwin even if we use relative rpaths for our own libraries,
2859   # as the install_name of libraries in extra_lib_dirs will point to their
2860   # location anyway.
2861   bin_install_rpaths += postgres_lib_d
2862   lib_install_rpaths += postgres_lib_d
2863   mod_install_rpaths += postgres_lib_d
2864 endif
2867 # Define arguments for default targets
2869 default_target_args = {
2870   'implicit_include_directories': false,
2871   'install': true,
2874 default_lib_args = default_target_args + {
2875   'name_prefix': '',
2878 internal_lib_args = default_lib_args + {
2879   'build_by_default': false,
2880   'install': false,
2883 default_mod_args = default_lib_args + {
2884   'name_prefix': '',
2885   'install_dir': dir_lib_pkg,
2888 default_bin_args = default_target_args + {
2889   'install_dir': dir_bin,
2892 if get_option('rpath')
2893   default_lib_args += {
2894     'install_rpath': ':'.join(lib_install_rpaths),
2895   }
2897   default_mod_args += {
2898     'install_rpath': ':'.join(mod_install_rpaths),
2899   }
2901   default_bin_args += {
2902     'install_rpath': ':'.join(bin_install_rpaths),
2903   }
2904 endif
2907 # Helper for exporting a limited number of symbols
2908 gen_export_kwargs = {
2909   'input': 'exports.txt',
2910   'output': '@BASENAME@.'+export_file_suffix,
2911   'command': [perl, files('src/tools/gen_export.pl'),
2912    '--format', export_file_format,
2913    '--input', '@INPUT0@', '--output', '@OUTPUT0@'],
2914   'build_by_default': false,
2915   'install': false,
2921 ### Helpers for custom targets used across the tree
2924 catalog_pm = files('src/backend/catalog/Catalog.pm')
2925 perfect_hash_pm = files('src/tools/PerfectHash.pm')
2926 gen_kwlist_deps = [perfect_hash_pm]
2927 gen_kwlist_cmd = [
2928   perl, '-I', '@SOURCE_ROOT@/src/tools',
2929   files('src/tools/gen_keywordlist.pl'),
2930   '--output', '@OUTDIR@', '@INPUT@']
2935 ### windows resources related stuff
2938 if host_system == 'windows'
2939   pg_ico = meson.source_root() / 'src' / 'port' / 'win32.ico'
2940   win32ver_rc = files('src/port/win32ver.rc')
2941   rcgen = find_program('src/tools/rcgen', native: true)
2943   rcgen_base_args = [
2944     '--srcdir', '@SOURCE_DIR@',
2945     '--builddir', meson.build_root(),
2946     '--rcout', '@OUTPUT0@',
2947     '--out', '@OUTPUT1@',
2948     '--input', '@INPUT@',
2949     '@EXTRA_ARGS@',
2950   ]
2952   if cc.get_argument_syntax() == 'msvc'
2953     rc = find_program('rc', required: true)
2954     rcgen_base_args += ['--rc', rc.path()]
2955     rcgen_outputs = ['@BASENAME@.rc', '@BASENAME@.res']
2956   else
2957     windres = find_program('windres', required: true)
2958     rcgen_base_args += ['--windres', windres.path()]
2959     rcgen_outputs = ['@BASENAME@.rc', '@BASENAME@.obj']
2960   endif
2962   # msbuild backend doesn't support this atm
2963   if meson.backend() == 'ninja'
2964     rcgen_base_args += ['--depfile', '@DEPFILE@']
2965   endif
2967   rcgen_bin_args = rcgen_base_args + [
2968     '--VFT_TYPE', 'VFT_APP',
2969     '--FILEENDING', 'exe',
2970     '--ICO', pg_ico
2971   ]
2973   rcgen_lib_args = rcgen_base_args + [
2974     '--VFT_TYPE', 'VFT_DLL',
2975     '--FILEENDING', 'dll',
2976   ]
2978   rc_bin_gen = generator(rcgen,
2979     depfile: '@BASENAME@.d',
2980     arguments: rcgen_bin_args,
2981     output: rcgen_outputs,
2982   )
2984   rc_lib_gen = generator(rcgen,
2985     depfile: '@BASENAME@.d',
2986     arguments: rcgen_lib_args,
2987     output: rcgen_outputs,
2988   )
2989 endif
2993 # headers that the whole build tree depends on
2994 generated_headers = []
2995 # headers that the backend build depends on
2996 generated_backend_headers = []
2997 # configure_files() output, needs a way of converting to file names
2998 configure_files = []
3000 # generated files that might conflict with a partial in-tree autoconf build
3001 generated_sources = []
3002 # same, for paths that differ between autoconf / meson builds
3003 # elements are [dir, [files]]
3004 generated_sources_ac = {}
3007 # First visit src/include - all targets creating headers are defined
3008 # within. That makes it easy to add the necessary dependencies for the
3009 # subsequent build steps.
3011 subdir('src/include')
3013 subdir('config')
3015 # Then through src/port and src/common, as most other things depend on them
3017 frontend_port_code = declare_dependency(
3018   compile_args: ['-DFRONTEND'],
3019   include_directories: [postgres_inc],
3020   dependencies: os_deps,
3023 backend_port_code = declare_dependency(
3024   compile_args: ['-DBUILDING_DLL'],
3025   include_directories: [postgres_inc],
3026   sources: [errcodes], # errcodes.h is needed due to use of ereport
3027   dependencies: os_deps,
3030 subdir('src/port')
3032 frontend_common_code = declare_dependency(
3033   compile_args: ['-DFRONTEND'],
3034   include_directories: [postgres_inc],
3035   sources: generated_headers,
3036   dependencies: [os_deps, zlib, zstd, lz4],
3039 backend_common_code = declare_dependency(
3040   compile_args: ['-DBUILDING_DLL'],
3041   include_directories: [postgres_inc],
3042   sources: generated_headers,
3043   dependencies: [os_deps, zlib, zstd],
3046 subdir('src/common')
3048 # all shared libraries should depend on shlib_code
3049 shlib_code = declare_dependency(
3050   link_args: ldflags_sl,
3053 # all static libraries not part of the backend should depend on this
3054 frontend_stlib_code = declare_dependency(
3055   include_directories: [postgres_inc],
3056   link_with: [common_static, pgport_static],
3057   sources: generated_headers,
3058   dependencies: [os_deps, libintl],
3061 # all shared libraries not part of the backend should depend on this
3062 frontend_shlib_code = declare_dependency(
3063   include_directories: [postgres_inc],
3064   link_with: [common_shlib, pgport_shlib],
3065   sources: generated_headers,
3066   dependencies: [shlib_code, os_deps, libintl],
3069 # Dependencies both for static and shared libpq
3070 libpq_deps += [
3071   thread_dep,
3073   gssapi,
3074   ldap_r,
3075   libintl,
3076   ssl,
3079 subdir('src/interfaces/libpq')
3080 # fe_utils depends on libpq
3081 subdir('src/fe_utils')
3083 # for frontend binaries
3084 frontend_code = declare_dependency(
3085   include_directories: [postgres_inc],
3086   link_with: [fe_utils, common_static, pgport_static],
3087   sources: generated_headers,
3088   dependencies: [os_deps, libintl],
3091 backend_both_deps += [
3092   thread_dep,
3093   bsd_auth,
3094   gssapi,
3095   icu,
3096   icu_i18n,
3097   ldap,
3098   libintl,
3099   libxml,
3100   lz4,
3101   pam,
3102   ssl,
3103   systemd,
3104   zlib,
3105   zstd,
3108 backend_mod_deps = backend_both_deps + os_deps
3110 backend_code = declare_dependency(
3111   compile_args: ['-DBUILDING_DLL'],
3112   include_directories: [postgres_inc],
3113   link_args: ldflags_be,
3114   link_with: [],
3115   sources: generated_headers + generated_backend_headers,
3116   dependencies: os_deps + backend_both_deps + backend_deps,
3119 # install these files only during test, not main install
3120 test_install_data = []
3121 test_install_libs = []
3123 # src/backend/meson.build defines backend_mod_code used for extension
3124 # libraries.
3127 # Then through the main sources. That way contrib can have dependencies on
3128 # main sources. Note that this explicitly doesn't enter src/test, right now a
3129 # few regression tests depend on contrib files.
3131 subdir('src')
3133 subdir('contrib')
3135 subdir('src/test')
3136 subdir('src/interfaces/libpq/test')
3137 subdir('src/interfaces/ecpg/test')
3139 subdir('doc/src/sgml')
3141 generated_sources_ac += {'': ['GNUmakefile']}
3143 # After processing src/test, add test_install_libs to the testprep_targets
3144 # to build them
3145 testprep_targets += test_install_libs
3148 # If there are any files in the source directory that we also generate in the
3149 # build directory, they might get preferred over the newly generated files,
3150 # e.g. because of a #include "file", which always will search in the current
3151 # directory first.
3152 message('checking for file conflicts between source and build directory')
3153 conflicting_files = []
3154 potentially_conflicting_files_t = []
3155 potentially_conflicting_files_t += generated_headers
3156 potentially_conflicting_files_t += generated_backend_headers
3157 potentially_conflicting_files_t += generated_backend_sources
3158 potentially_conflicting_files_t += generated_sources
3160 potentially_conflicting_files = []
3162 # convert all sources of potentially conflicting files into uniform shape
3163 foreach t : potentially_conflicting_files_t
3164   potentially_conflicting_files += t.full_path()
3165 endforeach
3166 foreach t1 : configure_files
3167   if meson.version().version_compare('>=0.59')
3168     t = fs.parent(t1) / fs.name(t1)
3169   else
3170     t = '@0@'.format(t1)
3171   endif
3172   potentially_conflicting_files += meson.current_build_dir() / t
3173 endforeach
3174 foreach sub, fnames : generated_sources_ac
3175   sub = meson.build_root() / sub
3176   foreach fname : fnames
3177     potentially_conflicting_files += sub / fname
3178   endforeach
3179 endforeach
3181 # find and report conflicting files
3182 foreach build_path : potentially_conflicting_files
3183   build_path = host_system == 'windows' ? fs.as_posix(build_path) : build_path
3184   # str.replace is in 0.56
3185   src_path = meson.current_source_dir() / build_path.split(meson.current_build_dir() / '')[1]
3186   if fs.exists(src_path) or fs.is_symlink(src_path)
3187     conflicting_files += src_path
3188   endif
3189 endforeach
3190 # XXX: Perhaps we should generate a file that would clean these up? The list
3191 # can be long.
3192 if conflicting_files.length() > 0
3193   errmsg_cleanup = '''
3194 Conflicting files in source directory:
3195   @0@
3197 The conflicting files need to be removed, either by removing the files listed
3198 above, or by running configure and then make maintainer-clean.
3200   errmsg_cleanup = errmsg_cleanup.format(' '.join(conflicting_files))
3201   error(errmsg_nonclean_base.format(errmsg_cleanup))
3202 endif
3206 ###############################################################
3207 # Install targets
3208 ###############################################################
3211 # We want to define additional install targets beyond what meson provides. For
3212 # that we need to define targets depending on nearly everything. We collected
3213 # the results of i18n.gettext() invocations into nls_targets, that also
3214 # includes maintainer targets though. Collect the ones we want as a dependency.
3216 # i18n.gettext() doesn't return the dependencies before 0.60 - but the gettext
3217 # generation happens during install, so that's not a real issue.
3218 nls_mo_targets = []
3219 if libintl.found() and meson.version().version_compare('>=0.60')
3220   # use range() to avoid the flattening of the list that foreach() would do
3221   foreach off : range(0, nls_targets.length())
3222     # i18n.gettext() list containing 1) list of built .mo files 2) maintainer
3223     # -pot target 3) maintainer -pot target
3224     nls_mo_targets += nls_targets[off][0]
3225   endforeach
3226   alias_target('nls', nls_mo_targets)
3227 endif
3230 all_built = [
3231   backend_targets,
3232   bin_targets,
3233   libpq_st,
3234   pl_targets,
3235   contrib_targets,
3236   nls_mo_targets,
3237   testprep_targets,
3238   ecpg_targets,
3241 # Meson's default install target is quite verbose. Provide one that is quiet.
3242 install_quiet = custom_target('install-quiet',
3243   output: 'install-quiet',
3244   build_always_stale: true,
3245   build_by_default: false,
3246   command: [meson_bin, meson_args, 'install', '--quiet', '--no-rebuild'],
3247   depends: all_built,
3250 # Target to install files used for tests, which aren't installed by default
3251 install_test_files_args = [
3252   install_files,
3253   '--prefix', dir_prefix,
3254   '--install', contrib_data_dir, test_install_data,
3255   '--install', dir_lib_pkg, test_install_libs,
3257 run_target('install-test-files',
3258   command: [python] + install_test_files_args,
3259   depends: testprep_targets,
3264 ###############################################################
3265 # Test prep
3266 ###############################################################
3268 # DESTDIR for the installation we'll run tests in
3269 test_install_destdir = meson.build_root() / 'tmp_install/'
3271 # DESTDIR + prefix appropriately munged
3272 if build_system != 'windows'
3273   # On unixoid systems this is trivial, we just prepend the destdir
3274   assert(dir_prefix.startswith('/')) # enforced by meson
3275   temp_install_bindir = '@0@@1@'.format(test_install_destdir, dir_prefix / dir_bin)
3276   temp_install_libdir = '@0@@1@'.format(test_install_destdir, dir_prefix / dir_lib)
3277 else
3278   # drives, drive-relative paths, etc make this complicated on windows, call
3279   # into a copy of meson's logic for it
3280   command = [
3281     python, '-c',
3282     'import sys; from pathlib import PurePath; d1=sys.argv[1]; d2=sys.argv[2]; print(str(PurePath(d1, *PurePath(d2).parts[1:])))',
3283     test_install_destdir]
3284   temp_install_bindir = run_command(command, dir_prefix / dir_bin, check: true).stdout().strip()
3285   temp_install_libdir = run_command(command, dir_prefix / dir_lib, check: true).stdout().strip()
3286 endif
3288 meson_install_args = meson_args + ['install'] + {
3289     'meson': ['--quiet', '--only-changed', '--no-rebuild'],
3290     'muon': []
3291 }[meson_impl]
3293 # setup tests should be run first,
3294 # so define priority for these
3295 setup_tests_priority = 100
3296 test('tmp_install',
3297     meson_bin, args: meson_install_args ,
3298     env: {'DESTDIR':test_install_destdir},
3299     priority: setup_tests_priority,
3300     timeout: 300,
3301     is_parallel: false,
3302     suite: ['setup'])
3304 test('install_test_files',
3305     python,
3306     args: install_test_files_args + ['--destdir', test_install_destdir],
3307     priority: setup_tests_priority,
3308     is_parallel: false,
3309     suite: ['setup'])
3311 test_result_dir = meson.build_root() / 'testrun'
3314 # XXX: pg_regress doesn't assign unique ports on windows. To avoid the
3315 # inevitable conflicts from running tests in parallel, hackishly assign
3316 # different ports for different tests.
3318 testport = 40000
3320 test_env = environment()
3322 test_initdb_template = meson.build_root() / 'tmp_install' / 'initdb-template'
3323 test_env.set('PG_REGRESS', pg_regress.full_path())
3324 test_env.set('REGRESS_SHLIB', regress_module.full_path())
3325 test_env.set('INITDB_TEMPLATE', test_initdb_template)
3327 # Test suites that are not safe by default but can be run if selected
3328 # by the user via the whitespace-separated list in variable PG_TEST_EXTRA.
3329 # Export PG_TEST_EXTRA so it can be checked in individual tap tests.
3330 test_env.set('PG_TEST_EXTRA', get_option('PG_TEST_EXTRA'))
3332 # Add the temporary installation to the library search path on platforms where
3333 # that works (everything but windows, basically). On windows everything
3334 # library-like gets installed into bindir, solving that issue.
3335 if library_path_var != ''
3336   test_env.prepend(library_path_var, temp_install_libdir)
3337 endif
3340 # Create (and remove old) initdb template directory. Tests use that, where
3341 # possible, to make it cheaper to run tests.
3343 # Use python to remove the old cached initdb, as we cannot rely on a working
3344 # 'rm' binary on windows.
3345 test('initdb_cache',
3346      python,
3347      args: [
3348        '-c', '''
3349 import shutil
3350 import sys
3351 import subprocess
3353 shutil.rmtree(sys.argv[1], ignore_errors=True)
3354 sp = subprocess.run(sys.argv[2:] + [sys.argv[1]])
3355 sys.exit(sp.returncode)
3356 ''',
3357        test_initdb_template,
3358        temp_install_bindir / 'initdb',
3359        '--auth', 'trust', '--no-sync', '--no-instructions', '--lc-messages=C',
3360        '--no-clean'
3361      ],
3362      priority: setup_tests_priority - 1,
3363      timeout: 300,
3364      is_parallel: false,
3365      env: test_env,
3366      suite: ['setup'])
3370 ###############################################################
3371 # Test Generation
3372 ###############################################################
3374 # When using a meson version understanding exclude_suites, define a
3375 # 'tmp_install' test setup (the default) that excludes tests running against a
3376 # pre-existing install and a 'running' setup that conflicts with creation of
3377 # the temporary installation and tap tests (which don't support running
3378 # against a running server).
3380 running_suites = []
3381 install_suites = []
3382 if meson.version().version_compare('>=0.57')
3383   runningcheck = true
3384 else
3385   runningcheck = false
3386 endif
3388 testwrap = files('src/tools/testwrap')
3390 foreach test_dir : tests
3391   testwrap_base = [
3392     testwrap,
3393     '--basedir', meson.build_root(),
3394     '--srcdir', test_dir['sd'],
3395   ]
3397   foreach kind, v : test_dir
3398     if kind in ['sd', 'bd', 'name']
3399       continue
3400     endif
3402     t = test_dir[kind]
3404     if kind in ['regress', 'isolation', 'ecpg']
3405       if kind == 'regress'
3406         runner = pg_regress
3407         fallback_dbname = 'regression_@0@'
3408       elif kind == 'isolation'
3409         runner = pg_isolation_regress
3410         fallback_dbname = 'isolation_regression_@0@'
3411       elif kind == 'ecpg'
3412         runner = pg_regress_ecpg
3413         fallback_dbname = 'ecpg_regression_@0@'
3414       endif
3416       test_group = test_dir['name']
3417       test_group_running = test_dir['name'] + '-running'
3419       test_output = test_result_dir / test_group / kind
3420       test_output_running = test_result_dir / test_group_running/ kind
3422       # Unless specified by the test, choose a non-conflicting database name,
3423       # to avoid conflicts when running against existing server.
3424       dbname = t.get('dbname',
3425         fallback_dbname.format(test_dir['name']))
3427       test_command_base = [
3428         runner.full_path(),
3429         '--inputdir', t.get('inputdir', test_dir['sd']),
3430         '--expecteddir', t.get('expecteddir', test_dir['sd']),
3431         '--bindir', '',
3432         '--dlpath', test_dir['bd'],
3433         '--max-concurrent-tests=20',
3434         '--dbname', dbname,
3435       ] + t.get('regress_args', [])
3437       test_selection = []
3438       if t.has_key('schedule')
3439         test_selection += ['--schedule', t['schedule'],]
3440       endif
3442       if kind == 'isolation'
3443         test_selection += t.get('specs', [])
3444       else
3445         test_selection += t.get('sql', [])
3446       endif
3448       env = test_env
3449       env.prepend('PATH', temp_install_bindir, test_dir['bd'])
3451       test_kwargs = {
3452         'protocol': 'tap',
3453         'priority': 10,
3454         'timeout': 1000,
3455         'depends': test_deps + t.get('deps', []),
3456         'env': env,
3457       } + t.get('test_kwargs', {})
3459       test(test_group / kind,
3460         python,
3461         args: [
3462           testwrap_base,
3463           '--testgroup', test_group,
3464           '--testname', kind,
3465           '--',
3466           test_command_base,
3467           '--outputdir', test_output,
3468           '--temp-instance', test_output / 'tmp_check',
3469           '--port', testport.to_string(),
3470           test_selection,
3471         ],
3472         suite: test_group,
3473         kwargs: test_kwargs,
3474       )
3475       install_suites += test_group
3477       # some tests can't support running against running DB
3478       if runningcheck and t.get('runningcheck', true)
3479         test(test_group_running / kind,
3480           python,
3481           args: [
3482             testwrap_base,
3483             '--testgroup', test_group_running,
3484             '--testname', kind,
3485             '--',
3486             test_command_base,
3487             '--outputdir', test_output_running,
3488             test_selection,
3489           ],
3490           is_parallel: t.get('runningcheck-parallel', true),
3491           suite: test_group_running,
3492           kwargs: test_kwargs,
3493         )
3494         running_suites += test_group_running
3495       endif
3497       testport += 1
3498     elif kind == 'tap'
3499       testwrap_tap = testwrap_base
3500       if not tap_tests_enabled
3501         testwrap_tap += ['--skip', 'TAP tests not enabled']
3502       endif
3504       test_command = [
3505         perl.path(),
3506         '-I', meson.source_root() / 'src/test/perl',
3507         '-I', test_dir['sd'],
3508       ]
3510       # Add temporary install, the build directory for non-installed binaries and
3511       # also test/ for non-installed test binaries built separately.
3512       env = test_env
3513       env.prepend('PATH', temp_install_bindir, test_dir['bd'], test_dir['bd'] / 'test')
3515       foreach name, value : t.get('env', {})
3516         env.set(name, value)
3517       endforeach
3519       test_group = test_dir['name']
3520       test_kwargs = {
3521         'protocol': 'tap',
3522         'suite': test_group,
3523         'timeout': 1000,
3524         'depends': test_deps + t.get('deps', []),
3525         'env': env,
3526       } + t.get('test_kwargs', {})
3528       foreach onetap : t['tests']
3529         # Make tap test names prettier, remove t/ and .pl
3530         onetap_p = onetap
3531         if onetap_p.startswith('t/')
3532           onetap_p = onetap.split('t/')[1]
3533         endif
3534         if onetap_p.endswith('.pl')
3535           onetap_p = fs.stem(onetap_p)
3536         endif
3538         test(test_dir['name'] / onetap_p,
3539           python,
3540           kwargs: test_kwargs,
3541           args: testwrap_tap + [
3542             '--testgroup', test_dir['name'],
3543             '--testname', onetap_p,
3544             '--', test_command,
3545             test_dir['sd'] / onetap,
3546           ],
3547         )
3548       endforeach
3549       install_suites += test_group
3550     else
3551       error('unknown kind @0@ of test in @1@'.format(kind, test_dir['sd']))
3552     endif
3554   endforeach # kinds of tests
3556 endforeach # directories with tests
3558 # repeat condition so meson realizes version dependency
3559 if meson.version().version_compare('>=0.57')
3560   add_test_setup('tmp_install',
3561     is_default: true,
3562     exclude_suites: running_suites)
3563   add_test_setup('running',
3564     exclude_suites: ['setup'] + install_suites)
3565 endif
3569 ###############################################################
3570 # Pseudo targets
3571 ###############################################################
3573 alias_target('backend', backend_targets)
3574 alias_target('bin', bin_targets + [libpq_st])
3575 alias_target('pl', pl_targets)
3576 alias_target('contrib', contrib_targets)
3577 alias_target('testprep', testprep_targets)
3579 alias_target('world', all_built, docs)
3580 alias_target('install-world', install_quiet, installdocs)
3582 run_target('help',
3583   command: [
3584     perl, '-ne', 'next if /^#/; print',
3585     files('doc/src/sgml/targets-meson.txt'),
3586   ]
3591 ###############################################################
3592 # Distribution archive
3593 ###############################################################
3595 # Meson has its own distribution building command (meson dist), but we
3596 # are not using that at this point.  The main problem is that, the way
3597 # they have implemented it, it is not deterministic.  Also, we want it
3598 # to be equivalent to the "make" version for the time being.  But the
3599 # target name "dist" in meson is reserved for that reason, so we call
3600 # the custom target "pgdist".
3602 git = find_program('git', required: false, native: true, disabler: true)
3603 bzip2 = find_program('bzip2', required: false, native: true)
3605 distdir = meson.project_name() + '-' + meson.project_version()
3607 pg_git_revision = get_option('PG_GIT_REVISION')
3609 # Note: core.autocrlf=false is needed to avoid line-ending conversion
3610 # in case the environment has a different setting.  Without this, a
3611 # tarball created on Windows might be different than on, and unusable
3612 # on, Unix machines.
3614 tar_gz = custom_target('tar.gz',
3615   build_always_stale: true,
3616   command: [git, '-C', '@SOURCE_ROOT@',
3617             '-c', 'core.autocrlf=false',
3618             'archive',
3619             '--format', 'tar.gz',
3620             '-9',
3621             '--prefix', distdir + '/',
3622             '-o', join_paths(meson.build_root(), '@OUTPUT@'),
3623             pg_git_revision],
3624   output: distdir + '.tar.gz',
3627 if bzip2.found()
3628   tar_bz2 = custom_target('tar.bz2',
3629     build_always_stale: true,
3630     command: [git, '-C', '@SOURCE_ROOT@',
3631               '-c', 'core.autocrlf=false',
3632               '-c', 'tar.tar.bz2.command="@0@" -c'.format(bzip2.path()),
3633               'archive',
3634               '--format', 'tar.bz2',
3635               '--prefix', distdir + '/',
3636               '-o', join_paths(meson.build_root(), '@OUTPUT@'),
3637               pg_git_revision],
3638     output: distdir + '.tar.bz2',
3639   )
3640 else
3641   tar_bz2 = custom_target('tar.bz2',
3642     command: [perl, '-e', 'exit 1'],
3643     output: distdir + '.tar.bz2',
3644   )
3645 endif
3647 alias_target('pgdist', [tar_gz, tar_bz2])
3649 # Make the standard "dist" command fail, to prevent accidental use.
3650 # But not if we are in a subproject, in case the parent project wants to
3651 # create a dist using the standard Meson command.
3652 if not meson.is_subproject()
3653   # We can only pass the identifier perl here when we depend on >= 0.55
3654   if meson.version().version_compare('>=0.55')
3655     meson.add_dist_script(perl, '-e', 'exit 1')
3656   endif
3657 endif
3661 ###############################################################
3662 # The End, The End, My Friend
3663 ###############################################################
3665 if meson.version().version_compare('>=0.57')
3667   summary(
3668     {
3669       'data block size': '@0@ kB'.format(cdata.get('BLCKSZ') / 1024),
3670       'WAL block size': '@0@ kB'.format(cdata.get('XLOG_BLCKSZ') / 1024),
3671       'segment size': get_option('segsize_blocks') != 0 ?
3672         '@0@ blocks'.format(cdata.get('RELSEG_SIZE')) :
3673         '@0@ GB'.format(get_option('segsize')),
3674     },
3675     section: 'Data layout',
3676   )
3678   summary(
3679     {
3680       'host system': '@0@ @1@'.format(host_system, host_cpu),
3681       'build system': '@0@ @1@'.format(build_machine.system(),
3682                                        build_machine.cpu_family()),
3683     },
3684     section: 'System',
3685   )
3687   summary(
3688     {
3689       'linker': '@0@'.format(cc.get_linker_id()),
3690       'C compiler': '@0@ @1@'.format(cc.get_id(), cc.version()),
3691     },
3692     section: 'Compiler',
3693   )
3695   summary(
3696     {
3697       'CPP FLAGS': ' '.join(cppflags),
3698       'C FLAGS, functional': ' '.join(cflags),
3699       'C FLAGS, warnings': ' '.join(cflags_warn),
3700       'C FLAGS, modules': ' '.join(cflags_mod),
3701       'C FLAGS, user specified': ' '.join(get_option('c_args')),
3702       'LD FLAGS': ' '.join(ldflags + get_option('c_link_args')),
3703     },
3704     section: 'Compiler Flags',
3705   )
3707   if llvm.found()
3708     summary(
3709       {
3710         'C++ compiler': '@0@ @1@'.format(cpp.get_id(), cpp.version()),
3711       },
3712       section: 'Compiler',
3713     )
3715     summary(
3716       {
3717         'C++ FLAGS, functional': ' '.join(cxxflags),
3718         'C++ FLAGS, warnings': ' '.join(cxxflags_warn),
3719         'C++ FLAGS, user specified': ' '.join(get_option('cpp_args')),
3720       },
3721       section: 'Compiler Flags',
3722     )
3723   endif
3725   summary(
3726     {
3727       'bison': '@0@ @1@'.format(bison.full_path(), bison_version),
3728       'dtrace': dtrace,
3729       'flex': '@0@ @1@'.format(flex.full_path(), flex_version),
3730     },
3731     section: 'Programs',
3732   )
3734   summary(
3735     {
3736       'bonjour': bonjour,
3737       'bsd_auth': bsd_auth,
3738       'docs': docs_dep,
3739       'docs_pdf': docs_pdf_dep,
3740       'gss': gssapi,
3741       'icu': icu,
3742       'ldap': ldap,
3743       'libxml': libxml,
3744       'libxslt': libxslt,
3745       'llvm': llvm,
3746       'lz4': lz4,
3747       'nls': libintl,
3748       'openssl': ssl,
3749       'pam': pam,
3750       'plperl': perl_dep,
3751       'plpython': python3_dep,
3752       'pltcl': tcl_dep,
3753       'readline': readline,
3754       'selinux': selinux,
3755       'systemd': systemd,
3756       'uuid': uuid,
3757       'zlib': zlib,
3758       'zstd': zstd,
3759     },
3760     section: 'External libraries',
3761   )
3763 endif