Fix incompatibilities with libxml2 >= 2.12.0.
[pgsql.git] / meson.build
blob8ed51b6aae8ba1e18ae6d97ca922c0f12dff2861
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: '17devel',
12   license: 'PostgreSQL',
14   # We want < 0.56 for python 3.5 compatibility on old platforms. EPEL for
15   # RHEL 7 has 0.55. < 0.54 would require replacing some uses of the fs
16   # module, < 0.53 all uses of fs. So far there's no need to go to >=0.56.
17   meson_version: '>=0.54',
18   default_options: [
19     'warning_level=1', #-Wall equivalent
20     'b_pch=false',
21     'buildtype=debugoptimized', # -O2 + debug
22     # For compatibility with the autoconf build, set a default prefix. This
23     # works even on windows, where it's a drive-relative path (i.e. when on
24     # d:/somepath it'll install to d:/usr/local/pgsql)
25     'prefix=/usr/local/pgsql',
26   ]
31 ###############################################################
32 # Basic prep
33 ###############################################################
35 fs = import('fs')
36 pkgconfig = import('pkgconfig')
38 host_system = host_machine.system()
39 build_system = build_machine.system()
40 host_cpu = host_machine.cpu_family()
42 cc = meson.get_compiler('c')
44 not_found_dep = dependency('', required: false)
45 thread_dep = dependency('threads')
46 auto_features = get_option('auto_features')
50 ###############################################################
51 # Safety first
52 ###############################################################
54 # It's very easy to get into confusing states when the source directory
55 # contains an in-place build. E.g. the wrong pg_config.h will be used. So just
56 # refuse to build in that case.
58 # There's a more elaborate check later, that checks for conflicts around all
59 # generated files. But we can only do that much further down the line, so this
60 # quick check seems worth it. Adhering to this advice should clean up the
61 # conflict, but won't protect against somebody doing make distclean or just
62 # removing pg_config.h
63 errmsg_nonclean_base = '''
64 ****
65 Non-clean source code directory detected.
67 To build with meson the source tree may not have an in-place, ./configure
68 style, build configured. You can have both meson and ./configure style builds
69 for the same source tree by building out-of-source / VPATH with
70 configure. Alternatively use a separate check out for meson based builds.
72 @0@
73 ****'''
74 if fs.exists(meson.current_source_dir() / 'src' / 'include' / 'pg_config.h')
75   errmsg_cleanup = 'To clean up, run make 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 == 'aix'
200   library_path_var = 'LIBPATH'
202   export_file_format = 'aix'
203   export_fmt = '-Wl,-bE:@0@'
204   mod_link_args_fmt = ['-Wl,-bI:@0@']
205   mod_link_with_dir = 'libdir'
206   mod_link_with_name = '@0@.imp'
208   # M:SRE sets a flag indicating that an object is a shared library. Seems to
209   # work in some circumstances without, but required in others.
210   ldflags_sl += '-Wl,-bM:SRE'
211   ldflags_be += '-Wl,-brtllib'
213   # Native memset() is faster, tested on:
214   # - AIX 5.1 and 5.2, XLC 6.0 (IBM's cc)
215   # - AIX 5.3 ML3, gcc 4.0.1
216   memset_loop_limit = 0
218 elif host_system == 'cygwin'
219   sema_kind = 'unnamed_posix'
220   cppflags += '-D_GNU_SOURCE'
221   dlsuffix = '.dll'
222   mod_link_args_fmt = ['@0@']
223   mod_link_with_name = 'lib@0@.exe.a'
224   mod_link_with_dir = 'libdir'
226 elif host_system == 'darwin'
227   dlsuffix = '.dylib'
228   library_path_var = 'DYLD_LIBRARY_PATH'
230   export_file_format = 'darwin'
231   export_fmt = '-Wl,-exported_symbols_list,@0@'
233   mod_link_args_fmt = ['-bundle_loader', '@0@']
234   mod_link_with_dir = 'bindir'
235   mod_link_with_name = '@0@'
237   sysroot_args = [files('src/tools/darwin_sysroot'), get_option('darwin_sysroot')]
238   pg_sysroot = run_command(sysroot_args, check:true).stdout().strip()
239   message('darwin sysroot: @0@'.format(pg_sysroot))
240   if pg_sysroot != ''
241     cflags += ['-isysroot', pg_sysroot]
242     ldflags += ['-isysroot', pg_sysroot]
243   endif
244   # meson defaults to -Wl,-undefined,dynamic_lookup for modules, which we
245   # don't want because a) it's different from what we do for autoconf, b) it
246   # causes warnings starting in macOS Ventura
247   ldflags_mod += ['-Wl,-undefined,error']
249 elif host_system == 'freebsd'
250   sema_kind = 'unnamed_posix'
252 elif host_system == 'linux'
253   sema_kind = 'unnamed_posix'
254   cppflags += '-D_GNU_SOURCE'
256 elif host_system == 'netbsd'
257   # We must resolve all dynamic linking in the core server at program start.
258   # Otherwise the postmaster can self-deadlock due to signals interrupting
259   # resolution of calls, since NetBSD's linker takes a lock while doing that
260   # and some postmaster signal handlers do things that will also acquire that
261   # lock.  As long as we need "-z now", might as well specify "-z relro" too.
262   # While there's not a hard reason to adopt these settings for our other
263   # executables, there's also little reason not to, so just add them to
264   # LDFLAGS.
265   ldflags += ['-Wl,-z,now', '-Wl,-z,relro']
267 elif host_system == 'openbsd'
268   # you're ok
270 elif host_system == 'sunos'
271   portname = 'solaris'
272   export_fmt = '-Wl,-M@0@'
273   cppflags += '-D_POSIX_PTHREAD_SEMANTICS'
275 elif host_system == 'windows'
276   portname = 'win32'
277   exesuffix = '.exe'
278   dlsuffix = '.dll'
279   library_path_var = ''
281   export_file_format = 'win'
282   export_file_suffix = 'def'
283   if cc.get_id() == 'msvc'
284     export_fmt = '/DEF:@0@'
285     mod_link_with_name = '@0@.exe.lib'
286   else
287     export_fmt = '@0@'
288     mod_link_with_name = 'lib@0@.exe.a'
289   endif
290   mod_link_args_fmt = ['@0@']
291   mod_link_with_dir = 'libdir'
293   shmem_kind = 'win32'
294   sema_kind = 'win32'
296   cdata.set('WIN32_STACK_RLIMIT', 4194304)
297   if cc.get_id() == 'msvc'
298     ldflags += '/INCREMENTAL:NO'
299     ldflags += '/STACK:@0@'.format(cdata.get('WIN32_STACK_RLIMIT'))
300     # ldflags += '/nxcompat' # generated by msbuild, should have it for ninja?
301   else
302     ldflags += '-Wl,--stack,@0@'.format(cdata.get('WIN32_STACK_RLIMIT'))
303     # Need to allow multiple definitions, we e.g. want to override getopt.
304     ldflags += '-Wl,--allow-multiple-definition'
305     # Ensure we get MSVC-like linking behavior.
306     ldflags += '-Wl,--disable-auto-import'
307   endif
309   os_deps += cc.find_library('ws2_32', required: true)
310   secur32_dep = cc.find_library('secur32', required: true)
311   backend_deps += secur32_dep
312   libpq_deps += secur32_dep
314   postgres_inc_d += 'src/include/port/win32'
315   if cc.get_id() == 'msvc'
316     postgres_inc_d += 'src/include/port/win32_msvc'
317   endif
319   windows = import('windows')
321 else
322   # XXX: Should we add an option to override the host_system as an escape
323   # hatch?
324   error('unknown host system: @0@'.format(host_system))
325 endif
329 ###############################################################
330 # Program paths
331 ###############################################################
333 # External programs
334 perl = find_program(get_option('PERL'), required: true, native: true)
335 python = find_program(get_option('PYTHON'), required: true, native: true)
336 flex = find_program(get_option('FLEX'), native: true, version: '>= 2.5.35')
337 bison = find_program(get_option('BISON'), native: true, version: '>= 2.3')
338 sed = find_program(get_option('SED'), 'sed', native: true, required: false)
339 prove = find_program(get_option('PROVE'), native: true, required: false)
340 tar = find_program(get_option('TAR'), native: true, required: false)
341 gzip = find_program(get_option('GZIP'), native: true, required: false)
342 program_lz4 = find_program(get_option('LZ4'), native: true, required: false)
343 openssl = find_program(get_option('OPENSSL'), native: true, required: false)
344 program_zstd = find_program(get_option('ZSTD'), native: true, required: false)
345 dtrace = find_program(get_option('DTRACE'), native: true, required: get_option('dtrace'))
346 missing = find_program('config/missing', native: true)
347 cp = find_program('cp', required: false, native: true)
348 xmllint_bin = find_program(get_option('XMLLINT'), native: true, required: false)
349 xsltproc_bin = find_program(get_option('XSLTPROC'), native: true, required: false)
351 bison_flags = []
352 if bison.found()
353   bison_version_c = run_command(bison, '--version', check: true)
354   # bison version string helpfully is something like
355   # >>bison (GNU bison) 3.8.1<<
356   bison_version = bison_version_c.stdout().split(' ')[3].split('\n')[0]
357   if bison_version.version_compare('>=3.0')
358     bison_flags += ['-Wno-deprecated']
359   endif
360 endif
361 bison_cmd = [bison, bison_flags, '-o', '@OUTPUT0@', '-d', '@INPUT@']
362 bison_kw = {
363   'output': ['@BASENAME@.c', '@BASENAME@.h'],
364   'command': bison_cmd,
367 flex_flags = []
368 if flex.found()
369   flex_version_c = run_command(flex, '--version', check: true)
370   flex_version = flex_version_c.stdout().split(' ')[1].split('\n')[0]
371 endif
372 flex_wrapper = files('src/tools/pgflex')
373 flex_cmd = [python, flex_wrapper,
374   '--builddir', '@BUILD_ROOT@',
375   '--srcdir', '@SOURCE_ROOT@',
376   '--privatedir', '@PRIVATE_DIR@',
377   '--flex', flex, '--perl', perl,
378   '-i', '@INPUT@', '-o', '@OUTPUT0@',
381 wget = find_program('wget', required: false, native: true)
382 wget_flags = ['-O', '@OUTPUT0@', '--no-use-server-timestamps']
384 install_files = files('src/tools/install_files')
388 ###############################################################
389 # Path to meson (for tests etc)
390 ###############################################################
392 # NB: this should really be part of meson, see
393 # https://github.com/mesonbuild/meson/issues/8511
394 meson_binpath_r = run_command(python, 'src/tools/find_meson', check: true)
396 if meson_binpath_r.stdout() == ''
397   error('huh, could not run find_meson.\nerrcode: @0@\nstdout: @1@\nstderr: @2@'.format(
398     meson_binpath_r.returncode(),
399     meson_binpath_r.stdout(),
400     meson_binpath_r.stderr()))
401 endif
403 meson_binpath_s = meson_binpath_r.stdout().split('\n')
404 meson_binpath_len = meson_binpath_s.length()
406 if meson_binpath_len < 1
407   error('unexpected introspect line @0@'.format(meson_binpath_r.stdout()))
408 endif
410 i = 0
411 meson_impl = ''
412 meson_binpath = ''
413 meson_args = []
414 foreach e : meson_binpath_s
415   if i == 0
416     meson_impl = e
417   elif i == 1
418     meson_binpath = e
419   else
420     meson_args += e
421   endif
422   i += 1
423 endforeach
425 if meson_impl not in ['muon', 'meson']
426   error('unknown meson implementation "@0@"'.format(meson_impl))
427 endif
429 meson_bin = find_program(meson_binpath, native: true)
433 ###############################################################
434 # Option Handling
435 ###############################################################
437 cdata.set('USE_ASSERT_CHECKING', get_option('cassert') ? 1 : false)
438 cdata.set('USE_INJECTION_POINTS', get_option('injection_points') ? 1 : false)
440 blocksize = get_option('blocksize').to_int() * 1024
442 if get_option('segsize_blocks') != 0
443   if get_option('segsize') != 1
444     warning('both segsize and segsize_blocks specified, segsize_blocks wins')
445   endif
447   segsize = get_option('segsize_blocks')
448 else
449   segsize = (get_option('segsize') * 1024 * 1024 * 1024) / blocksize
450 endif
452 cdata.set('BLCKSZ', blocksize, description:
453 '''Size of a disk block --- this also limits the size of a tuple. You can set
454    it bigger if you need bigger tuples (although TOAST should reduce the need
455    to have large tuples, since fields can be spread across multiple tuples).
456    BLCKSZ must be a power of 2. The maximum possible value of BLCKSZ is
457    currently 2^15 (32768). This is determined by the 15-bit widths of the
458    lp_off and lp_len fields in ItemIdData (see include/storage/itemid.h).
459    Changing BLCKSZ requires an initdb.''')
461 cdata.set('XLOG_BLCKSZ', get_option('wal_blocksize').to_int() * 1024)
462 cdata.set('RELSEG_SIZE', segsize)
463 cdata.set('DEF_PGPORT', get_option('pgport'))
464 cdata.set_quoted('DEF_PGPORT_STR', get_option('pgport').to_string())
465 cdata.set_quoted('PG_KRB_SRVNAM', get_option('krb_srvnam'))
466 if get_option('system_tzdata') != ''
467   cdata.set_quoted('SYSTEMTZDIR', get_option('system_tzdata'))
468 endif
472 ###############################################################
473 # Directories
474 ###############################################################
476 # These are set by the equivalent --xxxdir configure options.  We
477 # append "postgresql" to some of them, if the string does not already
478 # contain "pgsql" or "postgres", in order to avoid directory clutter.
480 pkg = 'postgresql'
482 dir_prefix = get_option('prefix')
484 dir_prefix_contains_pg = (dir_prefix.contains('pgsql') or dir_prefix.contains('postgres'))
486 dir_bin = get_option('bindir')
488 dir_data = get_option('datadir')
489 if not (dir_prefix_contains_pg or dir_data.contains('pgsql') or dir_data.contains('postgres'))
490   dir_data = dir_data / pkg
491 endif
493 dir_sysconf = get_option('sysconfdir')
494 if not (dir_prefix_contains_pg or dir_sysconf.contains('pgsql') or dir_sysconf.contains('postgres'))
495   dir_sysconf = dir_sysconf / pkg
496 endif
498 dir_lib = get_option('libdir')
500 dir_lib_pkg = dir_lib
501 if not (dir_prefix_contains_pg or dir_lib_pkg.contains('pgsql') or dir_lib_pkg.contains('postgres'))
502   dir_lib_pkg = dir_lib_pkg / pkg
503 endif
505 dir_pgxs = dir_lib_pkg / 'pgxs'
507 dir_include = get_option('includedir')
509 dir_include_pkg = dir_include
510 dir_include_pkg_rel = ''
511 if not (dir_prefix_contains_pg or dir_include_pkg.contains('pgsql') or dir_include_pkg.contains('postgres'))
512   dir_include_pkg = dir_include_pkg / pkg
513   dir_include_pkg_rel = pkg
514 endif
516 dir_man = get_option('mandir')
518 # FIXME: These used to be separately configurable - worth adding?
519 dir_doc = get_option('datadir') / 'doc'
520 if not (dir_prefix_contains_pg or dir_doc.contains('pgsql') or dir_doc.contains('postgres'))
521   dir_doc = dir_doc / pkg
522 endif
523 dir_doc_html = dir_doc / 'html'
525 dir_locale = get_option('localedir')
528 # Derived values
529 dir_bitcode = dir_lib_pkg / 'bitcode'
530 dir_include_internal = dir_include_pkg / 'internal'
531 dir_include_server = dir_include_pkg / 'server'
532 dir_include_extension = dir_include_server / 'extension'
533 dir_data_extension = dir_data / 'extension'
534 dir_doc_extension = dir_doc / 'extension'
538 ###############################################################
539 # Search paths, preparation for compiler tests
541 # NB: Arguments added later are not automatically used for subsequent
542 # configuration-time checks (so they are more isolated). If they should be
543 # used, they need to be added to test_c_args as well.
544 ###############################################################
546 postgres_inc = [include_directories(postgres_inc_d)]
547 test_lib_d = postgres_lib_d
548 test_c_args = cppflags + cflags
552 ###############################################################
553 # Library: bsd-auth
554 ###############################################################
556 bsd_authopt = get_option('bsd_auth')
557 bsd_auth = not_found_dep
558 if cc.check_header('bsd_auth.h', required: bsd_authopt,
559     args: test_c_args, include_directories: postgres_inc)
560   cdata.set('USE_BSD_AUTH', 1)
561   bsd_auth = declare_dependency()
562 endif
566 ###############################################################
567 # Library: bonjour
569 # For now don't search for DNSServiceRegister in a library - only Apple's
570 # Bonjour implementation, which is always linked, works.
571 ###############################################################
573 bonjouropt = get_option('bonjour')
574 bonjour = not_found_dep
575 if cc.check_header('dns_sd.h', required: bonjouropt,
576     args: test_c_args, include_directories: postgres_inc) and \
577    cc.has_function('DNSServiceRegister',
578     args: test_c_args, include_directories: postgres_inc)
579   cdata.set('USE_BONJOUR', 1)
580   bonjour = declare_dependency()
581 endif
585 ###############################################################
586 # Option: docs in HTML and man page format
587 ###############################################################
589 docs_opt = get_option('docs')
590 docs_dep = not_found_dep
591 if not docs_opt.disabled()
592   if xmllint_bin.found() and xsltproc_bin.found()
593     docs_dep = declare_dependency()
594   elif docs_opt.enabled()
595     error('missing required tools (xmllint and xsltproc needed) for docs in HTML / man page format')
596   endif
597 endif
601 ###############################################################
602 # Option: docs in PDF format
603 ###############################################################
605 docs_pdf_opt = get_option('docs_pdf')
606 docs_pdf_dep = not_found_dep
607 if not docs_pdf_opt.disabled()
608   fop = find_program(get_option('FOP'), native: true, required: docs_pdf_opt)
609   if xmllint_bin.found() and xsltproc_bin.found() and fop.found()
610     docs_pdf_dep = declare_dependency()
611   elif docs_pdf_opt.enabled()
612     error('missing required tools for docs in PDF format')
613   endif
614 endif
618 ###############################################################
619 # Library: GSSAPI
620 ###############################################################
622 gssapiopt = get_option('gssapi')
623 krb_srvtab = ''
624 have_gssapi = false
625 if not gssapiopt.disabled()
626   gssapi = dependency('krb5-gssapi', required: gssapiopt)
627   have_gssapi = gssapi.found()
629   if not have_gssapi
630   elif cc.check_header('gssapi/gssapi.h', dependencies: gssapi, required: false,
631       args: test_c_args, include_directories: postgres_inc)
632     cdata.set('HAVE_GSSAPI_GSSAPI_H', 1)
633   elif cc.check_header('gssapi.h', args: test_c_args, dependencies: gssapi, required: gssapiopt)
634     cdata.set('HAVE_GSSAPI_H', 1)
635   else
636     have_gssapi = false
637   endif
639   if not have_gssapi
640   elif cc.check_header('gssapi/gssapi_ext.h', dependencies: gssapi, required: false,
641       args: test_c_args, include_directories: postgres_inc)
642     cdata.set('HAVE_GSSAPI_GSSAPI_EXT_H', 1)
643   elif cc.check_header('gssapi_ext.h', args: test_c_args, dependencies: gssapi, required: gssapiopt)
644     cdata.set('HAVE_GSSAPI_EXT_H', 1)
645   else
646     have_gssapi = false
647   endif
649   if not have_gssapi
650   elif cc.has_function('gss_store_cred_into', dependencies: gssapi,
651       args: test_c_args, include_directories: postgres_inc)
652     cdata.set('ENABLE_GSS', 1)
654     krb_srvtab = 'FILE:/@0@/krb5.keytab)'.format(get_option('sysconfdir'))
655     cdata.set_quoted('PG_KRB_SRVTAB', krb_srvtab)
656   elif gssapiopt.enabled()
657     error('''could not find function 'gss_store_cred_into' required for GSSAPI''')
658   else
659     have_gssapi = false
660   endif
661 endif
662 if not have_gssapi
663   gssapi = not_found_dep
664 endif
668 ###############################################################
669 # Library: ldap
670 ###############################################################
672 ldapopt = get_option('ldap')
673 if ldapopt.disabled()
674   ldap = not_found_dep
675   ldap_r = not_found_dep
676 elif host_system == 'windows'
677   ldap = cc.find_library('wldap32', required: ldapopt)
678   ldap_r = ldap
679 else
680   # macos framework dependency is buggy for ldap (one can argue whether it's
681   # Apple's or meson's fault), leading to an endless recursion with ldap.h
682   # including itself. See https://github.com/mesonbuild/meson/issues/10002
683   # Luckily we only need pkg-config support, so the workaround isn't
684   # complicated.
685   ldap = dependency('ldap', method: 'pkg-config', required: false)
686   ldap_r = ldap
688   # Before 2.5 openldap didn't have a pkg-config file, and it might not be
689   # installed
690   if not ldap.found()
691     ldap = cc.find_library('ldap', required: ldapopt, dirs: test_lib_d,
692       has_headers: 'ldap.h', header_include_directories: postgres_inc)
694     # The separate ldap_r library only exists in OpenLDAP < 2.5, and if we
695     # have 2.5 or later, we shouldn't even probe for ldap_r (we might find a
696     # library from a separate OpenLDAP installation).  The most reliable
697     # way to check that is to check for a function introduced in 2.5.
698     if not ldap.found()
699       # don't have ldap, we shouldn't check for ldap_r
700     elif cc.has_function('ldap_verify_credentials',
701         dependencies: ldap, args: test_c_args)
702       ldap_r = ldap # ldap >= 2.5, no need for ldap_r
703     else
705       # Use ldap_r for FE if available, else assume ldap is thread-safe.
706       ldap_r = cc.find_library('ldap_r', required: false, dirs: test_lib_d,
707         has_headers: 'ldap.h', header_include_directories: postgres_inc)
708       if not ldap_r.found()
709         ldap_r = ldap
710       else
711         # On some platforms ldap_r fails to link without PTHREAD_LIBS.
712         ldap_r = declare_dependency(dependencies: [ldap_r, thread_dep])
713       endif
715       # PostgreSQL sometimes loads libldap_r and plain libldap into the same
716       # process.  Check for OpenLDAP versions known not to tolerate doing so;
717       # assume non-OpenLDAP implementations are safe.  The dblink test suite
718       # exercises the hazardous interaction directly.
719       compat_test_code = '''
720 #include <ldap.h>
721 #if !defined(LDAP_VENDOR_VERSION) || \
722      (defined(LDAP_API_FEATURE_X_OPENLDAP) && \
723       LDAP_VENDOR_VERSION >= 20424 && LDAP_VENDOR_VERSION <= 20431)
724 choke me
725 #endif
727       if not cc.compiles(compat_test_code,
728           name: 'LDAP implementation compatible',
729           dependencies: ldap, args: test_c_args)
730         warning('''
731 *** With OpenLDAP versions 2.4.24 through 2.4.31, inclusive, each backend
732 *** process that loads libpq (via WAL receiver, dblink, or postgres_fdw) and
733 *** also uses LDAP will crash on exit.''')
734       endif
735     endif
736   endif
738   if ldap.found() and cc.has_function('ldap_initialize',
739       dependencies: ldap, args: test_c_args)
740     cdata.set('HAVE_LDAP_INITIALIZE', 1)
741   endif
742 endif
744 if ldap.found()
745   assert(ldap_r.found())
746   cdata.set('USE_LDAP', 1)
747 else
748   assert(not ldap_r.found())
749 endif
753 ###############################################################
754 # Library: LLVM
755 ###############################################################
757 llvmopt = get_option('llvm')
758 llvm = not_found_dep
759 if add_languages('cpp', required: llvmopt, native: false)
760   llvm = dependency('llvm', version: '>=10', method: 'config-tool', required: llvmopt)
762   if llvm.found()
764     cdata.set('USE_LLVM', 1)
766     cpp = meson.get_compiler('cpp')
768     llvm_binpath = llvm.get_variable(configtool: 'bindir')
770     ccache = find_program('ccache', native: true, required: false)
771     clang = find_program(llvm_binpath / 'clang', required: true)
772   endif
773 elif llvmopt.auto()
774   message('llvm requires a C++ compiler')
775 endif
779 ###############################################################
780 # Library: icu
781 ###############################################################
783 icuopt = get_option('icu')
784 if not icuopt.disabled()
785   icu = dependency('icu-uc', required: icuopt)
786   icu_i18n = dependency('icu-i18n', required: icuopt)
788   if icu.found()
789     cdata.set('USE_ICU', 1)
790   endif
792 else
793   icu = not_found_dep
794   icu_i18n = not_found_dep
795 endif
799 ###############################################################
800 # Library: libxml
801 ###############################################################
803 libxmlopt = get_option('libxml')
804 if not libxmlopt.disabled()
805   libxml = dependency('libxml-2.0', required: libxmlopt, version: '>= 2.6.23')
807   if libxml.found()
808     cdata.set('USE_LIBXML', 1)
809   endif
810 else
811   libxml = not_found_dep
812 endif
816 ###############################################################
817 # Library: libxslt
818 ###############################################################
820 libxsltopt = get_option('libxslt')
821 if not libxsltopt.disabled()
822   libxslt = dependency('libxslt', required: libxsltopt)
824   if libxslt.found()
825     cdata.set('USE_LIBXSLT', 1)
826   endif
827 else
828   libxslt = not_found_dep
829 endif
833 ###############################################################
834 # Library: lz4
835 ###############################################################
837 lz4opt = get_option('lz4')
838 if not lz4opt.disabled()
839   lz4 = dependency('liblz4', required: lz4opt)
841   if lz4.found()
842     cdata.set('USE_LZ4', 1)
843     cdata.set('HAVE_LIBLZ4', 1)
844   endif
846 else
847   lz4 = not_found_dep
848 endif
852 ###############################################################
853 # Library: Tcl (for pltcl)
855 # NB: tclConfig.sh is used in autoconf build for getting
856 # TCL_SHARED_BUILD, TCL_INCLUDE_SPEC, TCL_LIBS and TCL_LIB_SPEC
857 # variables. For now we have not seen a need to copy
858 # that behaviour to the meson build.
859 ###############################################################
861 tclopt = get_option('pltcl')
862 tcl_version = get_option('tcl_version')
863 tcl_dep = not_found_dep
864 if not tclopt.disabled()
866   # via pkg-config
867   tcl_dep = dependency(tcl_version, required: false)
869   if not tcl_dep.found()
870     tcl_dep = cc.find_library(tcl_version,
871       required: tclopt,
872       dirs: test_lib_d)
873   endif
875   if not cc.has_header('tcl.h', dependencies: tcl_dep, required: tclopt)
876     tcl_dep = not_found_dep
877   endif
878 endif
882 ###############################################################
883 # Library: pam
884 ###############################################################
886 pamopt = get_option('pam')
887 if not pamopt.disabled()
888   pam = dependency('pam', required: false)
890   if not pam.found()
891     pam = cc.find_library('pam', required: pamopt, dirs: test_lib_d)
892   endif
894   if pam.found()
895     pam_header_found = false
897     # header file <security/pam_appl.h> or <pam/pam_appl.h> is required for PAM.
898     if cc.check_header('security/pam_appl.h', dependencies: pam, required: false,
899         args: test_c_args, include_directories: postgres_inc)
900       cdata.set('HAVE_SECURITY_PAM_APPL_H', 1)
901       pam_header_found = true
902     elif cc.check_header('pam/pam_appl.h', dependencies: pam, required: pamopt,
903         args: test_c_args, include_directories: postgres_inc)
904       cdata.set('HAVE_PAM_PAM_APPL_H', 1)
905       pam_header_found = true
906     endif
908     if pam_header_found
909       cdata.set('USE_PAM', 1)
910     else
911       pam = not_found_dep
912     endif
913   endif
914 else
915   pam = not_found_dep
916 endif
920 ###############################################################
921 # Library: Perl (for plperl)
922 ###############################################################
924 perlopt = get_option('plperl')
925 perl_dep = not_found_dep
926 if not perlopt.disabled()
927   perl_may_work = true
929   # First verify that perl has the necessary dependencies installed
930   perl_mods = run_command(
931     [perl,
932      '-MConfig', '-MOpcode', '-MExtUtils::Embed', '-MExtUtils::ParseXS',
933      '-e', ''],
934     check: false)
935   if perl_mods.returncode() != 0
936     perl_may_work = false
937     perl_msg = 'perl installation does not have the required modules'
938   endif
940   # Then inquire perl about its configuration
941   if perl_may_work
942     perl_conf_cmd = [perl, '-MConfig', '-e', 'print $Config{$ARGV[0]}']
943     perlversion = run_command(perl_conf_cmd, 'api_versionstring', check: true).stdout()
944     archlibexp = run_command(perl_conf_cmd, 'archlibexp', check: true).stdout()
945     privlibexp = run_command(perl_conf_cmd, 'privlibexp', check: true).stdout()
946     useshrplib = run_command(perl_conf_cmd, 'useshrplib', check: true).stdout()
948     perl_inc_dir = '@0@/CORE'.format(archlibexp)
950     if perlversion.version_compare('< 5.14')
951       perl_may_work = false
952       perl_msg = 'Perl version 5.14 or later is required, but this is @0@'.format(perlversion)
953     elif useshrplib != 'true'
954       perl_may_work = false
955       perl_msg = 'need a shared perl'
956     endif
957   endif
959   if perl_may_work
960     # On most platforms, archlibexp is also where the Perl include files live ...
961     perl_ccflags = ['-I@0@'.format(perl_inc_dir)]
962     # ... but on newer macOS versions, we must use -iwithsysroot to look
963     # under sysroot
964     if not fs.is_file('@0@/perl.h'.format(perl_inc_dir)) and \
965        fs.is_file('@0@@1@/perl.h'.format(pg_sysroot, perl_inc_dir))
966       perl_ccflags = ['-iwithsysroot', perl_inc_dir]
967     endif
969     # check compiler finds header
970     if not cc.has_header('perl.h', required: false,
971         args: test_c_args + perl_ccflags, include_directories: postgres_inc)
972       perl_may_work = false
973       perl_msg = 'missing perl.h'
974     endif
975   endif
977   if perl_may_work
978     perl_ccflags_r = run_command(perl_conf_cmd, 'ccflags', check: true).stdout()
980     # See comments for PGAC_CHECK_PERL_EMBED_CCFLAGS in perl.m4
981     foreach flag : perl_ccflags_r.split(' ')
982       if flag.startswith('-D') and \
983           (not flag.startswith('-D_') or flag == '_USE_32BIT_TIME_T')
984         perl_ccflags += flag
985       endif
986     endforeach
988     if host_system == 'windows'
989       perl_ccflags += ['-DPLPERL_HAVE_UID_GID']
991       if cc.get_id() == 'msvc'
992         # prevent binary mismatch between MSVC built plperl and Strawberry or
993         # msys ucrt perl libraries
994         perl_ccflags += ['-DNO_THREAD_SAFE_LOCALE']
995       endif
996     endif
998     message('CCFLAGS recommended by perl: @0@'.format(perl_ccflags_r))
999     message('CCFLAGS for embedding perl: @0@'.format(' '.join(perl_ccflags)))
1001     # We are after Embed's ldopts, but without the subset mentioned in
1002     # Config's ccdlflags and ldflags.  (Those are the choices of those who
1003     # built the Perl installation, which are not necessarily appropriate
1004     # for building PostgreSQL.)
1005     ldopts = run_command(perl, '-MExtUtils::Embed', '-e', 'ldopts', check: true).stdout().strip()
1006     undesired = run_command(perl_conf_cmd, 'ccdlflags', check: true).stdout().split()
1007     undesired += run_command(perl_conf_cmd, 'ldflags', check: true).stdout().split()
1009     perl_ldopts = []
1010     foreach ldopt : ldopts.split(' ')
1011       if ldopt == '' or ldopt in undesired
1012         continue
1013       endif
1015       perl_ldopts += ldopt.strip('"')
1016     endforeach
1018     message('LDFLAGS recommended by perl: "@0@"'.format(ldopts))
1019     message('LDFLAGS for embedding perl: "@0@"'.format(' '.join(perl_ldopts)))
1021     perl_dep_int = declare_dependency(
1022       compile_args: perl_ccflags,
1023       link_args: perl_ldopts,
1024       version: perlversion,
1025     )
1027     # While we're at it, check that we can link to libperl.
1028     # On most platforms, if perl.h is there then libperl.so will be too, but
1029     # at this writing Debian packages them separately.
1030     perl_link_test = '''
1031 /* see plperl.h */
1032 #ifdef _MSC_VER
1033 #define __inline__ inline
1034 #endif
1035 #include <EXTERN.h>
1036 #include <perl.h>
1037 int main(void)
1039 perl_alloc();
1040 }'''
1041     if not cc.links(perl_link_test, name: 'libperl',
1042           args: test_c_args + perl_ccflags + perl_ldopts,
1043           include_directories: postgres_inc)
1044       perl_may_work = false
1045       perl_msg = 'missing libperl'
1046     endif
1048   endif # perl_may_work
1050   if perl_may_work
1051     perl_dep = perl_dep_int
1052   else
1053     if perlopt.enabled()
1054       error('dependency plperl failed: @0@'.format(perl_msg))
1055     else
1056       message('disabling optional dependency plperl: @0@'.format(perl_msg))
1057     endif
1058   endif
1059 endif
1063 ###############################################################
1064 # Library: Python (for plpython)
1065 ###############################################################
1067 pyopt = get_option('plpython')
1068 python3_dep = not_found_dep
1069 if not pyopt.disabled()
1070   pm = import('python')
1071   python3_inst = pm.find_installation(python.path(), required: pyopt)
1072   if python3_inst.found()
1073     python3_dep = python3_inst.dependency(embed: true, required: pyopt)
1074     # Remove this check after we depend on Meson >= 1.1.0
1075     if not cc.check_header('Python.h', dependencies: python3_dep, required: pyopt)
1076       python3_dep = not_found_dep
1077     endif
1078   endif
1079 endif
1083 ###############################################################
1084 # Library: Readline
1085 ###############################################################
1087 if not get_option('readline').disabled()
1088   libedit_preferred = get_option('libedit_preferred')
1089   # Set the order of readline dependencies
1090   check_readline_deps = libedit_preferred ? \
1091     ['libedit', 'readline'] : ['readline', 'libedit']
1093   foreach readline_dep : check_readline_deps
1094     readline = dependency(readline_dep, required: false)
1095     if not readline.found()
1096       readline = cc.find_library(readline_dep,
1097         required: get_option('readline'),
1098         dirs: test_lib_d)
1099     endif
1100     if readline.found()
1101       break
1102     endif
1103   endforeach
1105   if readline.found()
1106     cdata.set('HAVE_LIBREADLINE', 1)
1108     editline_prefix = {
1109       'header_prefix': 'editline/',
1110       'flag_prefix': 'EDITLINE_',
1111     }
1112     readline_prefix = {
1113       'header_prefix': 'readline/',
1114       'flag_prefix': 'READLINE_',
1115     }
1116     default_prefix = {
1117       'header_prefix': '',
1118       'flag_prefix': '',
1119     }
1121     # Set the order of prefixes
1122     prefixes = libedit_preferred ? \
1123       [editline_prefix, default_prefix, readline_prefix] : \
1124       [readline_prefix, default_prefix, editline_prefix]
1126     at_least_one_header_found = false
1127     foreach header : ['history', 'readline']
1128       is_found = false
1129       foreach prefix : prefixes
1130         header_file = '@0@@1@.h'.format(prefix['header_prefix'], header)
1131         # Check history.h and readline.h
1132         if not is_found and cc.has_header(header_file,
1133             args: test_c_args, include_directories: postgres_inc,
1134             dependencies: [readline], required: false)
1135           if header == 'readline'
1136             readline_h = header_file
1137           endif
1138           cdata.set('HAVE_@0@@1@_H'.format(prefix['flag_prefix'], header).to_upper(), 1)
1139           is_found = true
1140           at_least_one_header_found = true
1141         endif
1142       endforeach
1143     endforeach
1145     if not at_least_one_header_found
1146       error('''readline header not found
1147 If you have @0@ already installed, see meson-logs/meson-log.txt for details on the
1148 failure. It is possible the compiler isn't looking in the proper directory.
1149 Use -Dreadline=disabled to disable readline support.'''.format(readline_dep))
1150     endif
1152     check_funcs = [
1153       'append_history',
1154       'history_truncate_file',
1155       'rl_completion_matches',
1156       'rl_filename_completion_function',
1157       'rl_reset_screen_size',
1158       'rl_variable_bind',
1159     ]
1161     foreach func : check_funcs
1162       found = cc.has_function(func, dependencies: [readline],
1163         args: test_c_args, include_directories: postgres_inc)
1164       cdata.set('HAVE_' + func.to_upper(), found ? 1 : false)
1165     endforeach
1167     check_vars = [
1168       'rl_completion_suppress_quote',
1169       'rl_filename_quote_characters',
1170       'rl_filename_quoting_function',
1171     ]
1173     foreach var : check_vars
1174       cdata.set('HAVE_' + var.to_upper(),
1175         cc.has_header_symbol(readline_h, var,
1176           args: test_c_args, include_directories: postgres_inc,
1177           prefix: '#include <stdio.h>',
1178           dependencies: [readline]) ? 1 : false)
1179     endforeach
1181     # If found via cc.find_library() ensure headers are found when using the
1182     # dependency. On meson < 0.57 one cannot do compiler checks using the
1183     # dependency returned by declare_dependency(), so we can't do this above.
1184     if readline.type_name() == 'library'
1185       readline = declare_dependency(dependencies: readline,
1186         include_directories: postgres_inc)
1187     endif
1189     # On windows with mingw readline requires auto-import to successfully
1190     # link, as the headers don't use declspec(dllimport)
1191     if host_system == 'windows' and cc.get_id() != 'msvc'
1192       readline = declare_dependency(dependencies: readline,
1193         link_args: '-Wl,--enable-auto-import')
1194     endif
1195   endif
1197   # XXX: Figure out whether to implement mingw warning equivalent
1198 else
1199   readline = not_found_dep
1200 endif
1204 ###############################################################
1205 # Library: selinux
1206 ###############################################################
1208 selinux = not_found_dep
1209 selinuxopt = get_option('selinux')
1210 if meson.version().version_compare('>=0.59')
1211   selinuxopt = selinuxopt.disable_auto_if(host_system != 'linux')
1212 endif
1213 selinux = dependency('libselinux', required: selinuxopt, version: '>= 2.1.10')
1214 cdata.set('HAVE_LIBSELINUX',
1215   selinux.found() ? 1 : false)
1219 ###############################################################
1220 # Library: systemd
1221 ###############################################################
1223 systemd = not_found_dep
1224 systemdopt = get_option('systemd')
1225 if meson.version().version_compare('>=0.59')
1226   systemdopt = systemdopt.disable_auto_if(host_system != 'linux')
1227 endif
1228 systemd = dependency('libsystemd', required: systemdopt)
1229 cdata.set('USE_SYSTEMD', systemd.found() ? 1 : false)
1233 ###############################################################
1234 # Library: SSL
1235 ###############################################################
1237 ssl = not_found_dep
1238 ssl_library = 'none'
1239 sslopt = get_option('ssl')
1241 if sslopt == 'auto' and auto_features.disabled()
1242   sslopt = 'none'
1243 endif
1245 if sslopt in ['auto', 'openssl']
1246   openssl_required = (sslopt == 'openssl')
1248   # Try to find openssl via pkg-config et al, if that doesn't work
1249   # (e.g. because it's provided as part of the OS, like on FreeBSD), look for
1250   # the library names that we know about.
1252   # via pkg-config et al
1253   ssl = dependency('openssl', required: false)
1254   # only meson >= 0.57 supports declare_dependency() in cc.has_function(), so
1255   # we pass cc.find_library() results if necessary
1256   ssl_int = []
1258   # via library + headers
1259   if not ssl.found()
1260     ssl_lib = cc.find_library('ssl',
1261       dirs: test_lib_d,
1262       header_include_directories: postgres_inc,
1263       has_headers: ['openssl/ssl.h', 'openssl/err.h'],
1264       required: openssl_required)
1265     crypto_lib = cc.find_library('crypto',
1266       dirs: test_lib_d,
1267       required: openssl_required)
1268     if ssl_lib.found() and crypto_lib.found()
1269       ssl_int = [ssl_lib, crypto_lib]
1270       ssl = declare_dependency(dependencies: ssl_int, include_directories: postgres_inc)
1271     endif
1272   elif cc.has_header('openssl/ssl.h', args: test_c_args, dependencies: ssl, required: openssl_required) and \
1273        cc.has_header('openssl/err.h', args: test_c_args, dependencies: ssl, required: openssl_required)
1274     ssl_int = [ssl]
1275   else
1276     ssl = not_found_dep
1277   endif
1279   if ssl.found()
1280     check_funcs = [
1281       ['CRYPTO_new_ex_data', {'required': true}],
1282       ['SSL_new', {'required': true}],
1284       # Function introduced in OpenSSL 1.0.2, not in LibreSSL.
1285       ['SSL_CTX_set_cert_cb'],
1287       # Functions introduced in OpenSSL 1.1.0. We used to check for
1288       # OPENSSL_VERSION_NUMBER, but that didn't work with 1.1.0, because LibreSSL
1289       # defines OPENSSL_VERSION_NUMBER to claim version 2.0.0, even though it
1290       # doesn't have these OpenSSL 1.1.0 functions. So check for individual
1291       # functions.
1292       ['OPENSSL_init_ssl'],
1293       ['BIO_meth_new'],
1294       ['ASN1_STRING_get0_data'],
1295       ['HMAC_CTX_new'],
1296       ['HMAC_CTX_free'],
1298       # OpenSSL versions before 1.1.0 required setting callback functions, for
1299       # thread-safety. In 1.1.0, it's no longer required, and CRYPTO_lock()
1300       # function was removed.
1301       ['CRYPTO_lock'],
1303       # Function introduced in OpenSSL 1.1.1
1304       ['X509_get_signature_info'],
1305     ]
1307     are_openssl_funcs_complete = true
1308     foreach c : check_funcs
1309       func = c.get(0)
1310       val = cc.has_function(func, args: test_c_args, dependencies: ssl_int)
1311       required = c.get(1, {}).get('required', false)
1312       if required and not val
1313         are_openssl_funcs_complete = false
1314         if openssl_required
1315           error('openssl function @0@ is required'.format(func))
1316         endif
1317         break
1318       elif not required
1319         cdata.set('HAVE_' + func.to_upper(), val ? 1 : false)
1320       endif
1321     endforeach
1323     if are_openssl_funcs_complete
1324       cdata.set('USE_OPENSSL', 1,
1325                 description: 'Define to 1 to build with OpenSSL support. (-Dssl=openssl)')
1326       cdata.set('OPENSSL_API_COMPAT', '0x10002000L',
1327                 description: 'Define to the OpenSSL API version in use. This avoids deprecation warnings from newer OpenSSL versions.')
1328       ssl_library = 'openssl'
1329     else
1330       ssl = not_found_dep
1331     endif
1332   endif
1333 endif
1335 if sslopt == 'auto' and auto_features.enabled() and not ssl.found()
1336   error('no SSL library found')
1337 endif
1341 ###############################################################
1342 # Library: uuid
1343 ###############################################################
1345 uuidopt = get_option('uuid')
1346 if uuidopt != 'none'
1347   uuidname = uuidopt.to_upper()
1348   if uuidopt == 'e2fs'
1349     uuid = dependency('uuid', required: true)
1350     uuidfunc = 'uuid_generate'
1351     uuidheader = 'uuid/uuid.h'
1352   elif uuidopt == 'bsd'
1353     # libc should have uuid function
1354     uuid = declare_dependency()
1355     uuidfunc = 'uuid_to_string'
1356     uuidheader = 'uuid.h'
1357   elif uuidopt == 'ossp'
1358     uuid = dependency('ossp-uuid', required: true)
1359     uuidfunc = 'uuid_export'
1360     uuidheader = 'uuid.h'
1361   else
1362     error('unknown uuid build option value: @0@'.format(uuidopt))
1363   endif
1365   if not cc.has_header_symbol(uuidheader, uuidfunc, args: test_c_args, dependencies: uuid)
1366     error('uuid library @0@ missing required function @1@'.format(uuidopt, uuidfunc))
1367   endif
1368   cdata.set('HAVE_@0@'.format(uuidheader.underscorify().to_upper()), 1)
1370   cdata.set('HAVE_UUID_@0@'.format(uuidname), 1,
1371            description: 'Define to 1 if you have @0@ UUID support.'.format(uuidname))
1372 else
1373   uuid = not_found_dep
1374 endif
1378 ###############################################################
1379 # Library: zlib
1380 ###############################################################
1382 zlibopt = get_option('zlib')
1383 zlib = not_found_dep
1384 if not zlibopt.disabled()
1385   zlib_t = dependency('zlib', required: zlibopt)
1387   if zlib_t.type_name() == 'internal'
1388     # if fallback was used, we don't need to test if headers are present (they
1389     # aren't built yet, so we can't test)
1390     zlib = zlib_t
1391   elif not zlib_t.found()
1392     warning('did not find zlib')
1393   elif not cc.has_header('zlib.h',
1394       args: test_c_args, include_directories: postgres_inc,
1395       dependencies: [zlib_t], required: zlibopt)
1396     warning('zlib header not found')
1397   else
1398     zlib = zlib_t
1399   endif
1401   if zlib.found()
1402     cdata.set('HAVE_LIBZ', 1)
1403   endif
1404 endif
1408 ###############################################################
1409 # Library: tap test dependencies
1410 ###############################################################
1412 # Check whether tap tests are enabled or not
1413 tap_tests_enabled = false
1414 tapopt = get_option('tap_tests')
1415 if not tapopt.disabled()
1416   # Checking for perl modules for tap tests
1417   perl_ipc_run_check = run_command(perl, 'config/check_modules.pl', check: false)
1418   if perl_ipc_run_check.returncode() != 0
1419     message(perl_ipc_run_check.stderr().strip())
1420     if tapopt.enabled()
1421       error('Additional Perl modules are required to run TAP tests.')
1422     else
1423       warning('Additional Perl modules are required to run TAP tests.')
1424     endif
1425   else
1426     tap_tests_enabled = true
1427   endif
1428 endif
1432 ###############################################################
1433 # Library: zstd
1434 ###############################################################
1436 zstdopt = get_option('zstd')
1437 if not zstdopt.disabled()
1438   zstd = dependency('libzstd', required: zstdopt, version: '>=1.4.0')
1440   if zstd.found()
1441     cdata.set('USE_ZSTD', 1)
1442     cdata.set('HAVE_LIBZSTD', 1)
1443   endif
1445 else
1446   zstd = not_found_dep
1447 endif
1451 ###############################################################
1452 # Compiler tests
1453 ###############################################################
1455 # Do we need -std=c99 to compile C99 code? We don't want to add -std=c99
1456 # unnecessarily, because we optionally rely on newer features.
1457 c99_test = '''
1458 #include <stdbool.h>
1459 #include <complex.h>
1460 #include <tgmath.h>
1461 #include <inttypes.h>
1463 struct named_init_test {
1464   int a;
1465   int b;
1468 extern void structfunc(struct named_init_test);
1470 int main(int argc, char **argv)
1472   struct named_init_test nit = {
1473     .a = 3,
1474     .b = 5,
1475   };
1477   for (int loop_var = 0; loop_var < 3; loop_var++)
1478   {
1479     nit.a += nit.b;
1480   }
1482   structfunc((struct named_init_test){1, 0});
1484   return nit.a != 0;
1488 if not cc.compiles(c99_test, name: 'c99', args: test_c_args)
1489   if cc.compiles(c99_test, name: 'c99 with -std=c99',
1490         args: test_c_args + ['-std=c99'])
1491     test_c_args += '-std=c99'
1492     cflags += '-std=c99'
1493   else
1494     error('C compiler does not support C99')
1495   endif
1496 endif
1498 sizeof_long = cc.sizeof('long', args: test_c_args)
1499 cdata.set('SIZEOF_LONG', sizeof_long)
1500 if sizeof_long == 8
1501   cdata.set('HAVE_LONG_INT_64', 1)
1502   cdata.set('PG_INT64_TYPE', 'long int')
1503   cdata.set_quoted('INT64_MODIFIER', 'l')
1504 elif sizeof_long == 4 and cc.sizeof('long long', args: test_c_args) == 8
1505   cdata.set('HAVE_LONG_LONG_INT_64', 1)
1506   cdata.set('PG_INT64_TYPE', 'long long int')
1507   cdata.set_quoted('INT64_MODIFIER', 'll')
1508 else
1509   error('do not know how to get a 64bit int')
1510 endif
1512 if host_machine.endian() == 'big'
1513   cdata.set('WORDS_BIGENDIAN', 1)
1514 endif
1516 alignof_types = ['short', 'int', 'long', 'double']
1517 maxalign = 0
1518 foreach t : alignof_types
1519   align = cc.alignment(t, args: test_c_args)
1520   if maxalign < align
1521     maxalign = align
1522   endif
1523   cdata.set('ALIGNOF_@0@'.format(t.to_upper()), align)
1524 endforeach
1525 cdata.set('MAXIMUM_ALIGNOF', maxalign)
1527 cdata.set('SIZEOF_VOID_P', cc.sizeof('void *', args: test_c_args))
1528 cdata.set('SIZEOF_SIZE_T', cc.sizeof('size_t', args: test_c_args))
1531 # Check if __int128 is a working 128 bit integer type, and if so
1532 # define PG_INT128_TYPE to that typename.
1534 # This currently only detects a GCC/clang extension, but support for other
1535 # environments may be added in the future.
1537 # For the moment we only test for support for 128bit math; support for
1538 # 128bit literals and snprintf is not required.
1539 if cc.links('''
1540   /*
1541    * We don't actually run this test, just link it to verify that any support
1542    * functions needed for __int128 are present.
1543    *
1544    * These are globals to discourage the compiler from folding all the
1545    * arithmetic tests down to compile-time constants.  We do not have
1546    * convenient support for 128bit literals at this point...
1547    */
1548   __int128 a = 48828125;
1549   __int128 b = 97656250;
1551   int main(void)
1552   {
1553       __int128 c,d;
1554       a = (a << 12) + 1; /* 200000000001 */
1555       b = (b << 12) + 5; /* 400000000005 */
1556       /* try the most relevant arithmetic ops */
1557       c = a * b;
1558       d = (c + b) / b;
1559       /* must use the results, else compiler may optimize arithmetic away */
1560       return d != a+1;
1561   }''',
1562   name: '__int128',
1563   args: test_c_args)
1565   buggy_int128 = false
1567   # Use of non-default alignment with __int128 tickles bugs in some compilers.
1568   # If not cross-compiling, we can test for bugs and disable use of __int128
1569   # with buggy compilers.  If cross-compiling, hope for the best.
1570   # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83925
1571   if not meson.is_cross_build()
1572     r = cc.run('''
1573     /* This must match the corresponding code in c.h: */
1574     #if defined(__GNUC__) || defined(__SUNPRO_C) || defined(__IBMC__)
1575     #define pg_attribute_aligned(a) __attribute__((aligned(a)))
1576     #elif defined(_MSC_VER)
1577     #define pg_attribute_aligned(a) __declspec(align(a))
1578     #endif
1579     typedef __int128 int128a
1580     #if defined(pg_attribute_aligned)
1581     pg_attribute_aligned(8)
1582     #endif
1583     ;
1585     int128a holder;
1586     void pass_by_val(void *buffer, int128a par) { holder = par; }
1588     int main(void)
1589     {
1590         long int i64 = 97656225L << 12;
1591         int128a q;
1592         pass_by_val(main, (int128a) i64);
1593         q = (int128a) i64;
1594         return q != holder;
1595     }''',
1596     name: '__int128 alignment bug',
1597     args: test_c_args)
1598     assert(r.compiled())
1599     if r.returncode() != 0
1600       buggy_int128 = true
1601       message('__int128 support present but buggy and thus disabled')
1602     endif
1603   endif
1605   if not buggy_int128
1606     cdata.set('PG_INT128_TYPE', '__int128')
1607     cdata.set('ALIGNOF_PG_INT128_TYPE', cc.alignment('__int128', args: test_c_args))
1608   endif
1609 endif
1612 # Check if the C compiler knows computed gotos (gcc extension, also
1613 # available in at least clang).  If so, define HAVE_COMPUTED_GOTO.
1615 # Checking whether computed gotos are supported syntax-wise ought to
1616 # be enough, as the syntax is otherwise illegal.
1617 if cc.compiles('''
1618     static inline int foo(void)
1619     {
1620       void *labeladdrs[] = {&&my_label};
1621       goto *labeladdrs[0];
1622       my_label:
1623       return 1;
1624     }''',
1625     args: test_c_args)
1626   cdata.set('HAVE_COMPUTED_GOTO', 1)
1627 endif
1630 # Check if the C compiler understands _Static_assert(),
1631 # and define HAVE__STATIC_ASSERT if so.
1633 # We actually check the syntax ({ _Static_assert(...) }), because we need
1634 # gcc-style compound expressions to be able to wrap the thing into macros.
1635 if cc.compiles('''
1636     int main(int arg, char **argv)
1637     {
1638         ({ _Static_assert(1, "foo"); });
1639     }
1640     ''',
1641     args: test_c_args)
1642   cdata.set('HAVE__STATIC_ASSERT', 1)
1643 endif
1646 # We use <stdbool.h> if we have it and it declares type bool as having
1647 # size 1.  Otherwise, c.h will fall back to declaring bool as unsigned char.
1648 if cc.has_type('_Bool', args: test_c_args) \
1649     and cc.has_type('bool', prefix: '#include <stdbool.h>', args: test_c_args) \
1650     and cc.sizeof('bool', prefix: '#include <stdbool.h>', args: test_c_args) == 1
1651   cdata.set('HAVE__BOOL', 1)
1652   cdata.set('PG_USE_STDBOOL', 1)
1653 endif
1656 # Need to check a call with %m because netbsd supports gnu_printf but emits a
1657 # warning for each use of %m.
1658 printf_attributes = ['gnu_printf', '__syslog__', 'printf']
1659 testsrc = '''
1660 extern void emit_log(int ignore, const char *fmt,...) __attribute__((format(@0@, 2,3)));
1661 static void call_log(void)
1663     emit_log(0, "error: %s: %m", "foo");
1666 attrib_error_args = cc.get_supported_arguments('-Werror=format', '-Werror=ignored-attributes')
1667 foreach a : printf_attributes
1668   if cc.compiles(testsrc.format(a),
1669       args: test_c_args + attrib_error_args, name: 'format ' + a)
1670     cdata.set('PG_PRINTF_ATTRIBUTE', a)
1671     break
1672   endif
1673 endforeach
1676 if cc.has_function_attribute('visibility:default') and \
1677     cc.has_function_attribute('visibility:hidden')
1678   cdata.set('HAVE_VISIBILITY_ATTRIBUTE', 1)
1680   # Only newer versions of meson know not to apply gnu_symbol_visibility =
1681   # inlineshidden to C code as well... And either way, we want to put these
1682   # flags into exported files (pgxs, .pc files).
1683   cflags_mod += '-fvisibility=hidden'
1684   cxxflags_mod += ['-fvisibility=hidden', '-fvisibility-inlines-hidden']
1685   ldflags_mod += '-fvisibility=hidden'
1686 endif
1689 # Check if various builtins exist. Some builtins are tested separately,
1690 # because we want to test something more complicated than the generic case.
1691 builtins = [
1692   'bswap16',
1693   'bswap32',
1694   'bswap64',
1695   'clz',
1696   'ctz',
1697   'constant_p',
1698   'frame_address',
1699   'popcount',
1700   'unreachable',
1703 foreach builtin : builtins
1704   fname = '__builtin_@0@'.format(builtin)
1705   if cc.has_function(fname, args: test_c_args)
1706     cdata.set('HAVE@0@'.format(fname.to_upper()), 1)
1707   endif
1708 endforeach
1711 # Check if the C compiler understands __builtin_types_compatible_p,
1712 # and define HAVE__BUILTIN_TYPES_COMPATIBLE_P if so.
1714 # We check usage with __typeof__, though it's unlikely any compiler would
1715 # have the former and not the latter.
1716 if cc.compiles('''
1717     static int x;
1718     static int y[__builtin_types_compatible_p(__typeof__(x), int)];
1719     ''',
1720     name: '__builtin_types_compatible_p',
1721     args: test_c_args)
1722   cdata.set('HAVE__BUILTIN_TYPES_COMPATIBLE_P', 1)
1723 endif
1726 # Check if the C compiler understands __builtin_$op_overflow(),
1727 # and define HAVE__BUILTIN_OP_OVERFLOW if so.
1729 # Check for the most complicated case, 64 bit multiplication, as a
1730 # proxy for all of the operations.  To detect the case where the compiler
1731 # knows the function but library support is missing, we must link not just
1732 # compile, and store the results in global variables so the compiler doesn't
1733 # optimize away the call.
1734 if cc.links('''
1735     INT64 a = 1;
1736     INT64 b = 1;
1737     INT64 result;
1739     int main(void)
1740     {
1741         return __builtin_mul_overflow(a, b, &result);
1742     }''',
1743     name: '__builtin_mul_overflow',
1744     args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))],
1745     )
1746   cdata.set('HAVE__BUILTIN_OP_OVERFLOW', 1)
1747 endif
1750 # XXX: The configure.ac check for __cpuid() is broken, we don't copy that
1751 # here. To prevent problems due to two detection methods working, stop
1752 # checking after one.
1753 if cc.links('''
1754     #include <cpuid.h>
1755     int main(int arg, char **argv)
1756     {
1757         unsigned int exx[4] = {0, 0, 0, 0};
1758         __get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
1759     }
1760     ''', name: '__get_cpuid',
1761     args: test_c_args)
1762   cdata.set('HAVE__GET_CPUID', 1)
1763 elif cc.links('''
1764     #include <intrin.h>
1765     int main(int arg, char **argv)
1766     {
1767         unsigned int exx[4] = {0, 0, 0, 0};
1768         __cpuid(exx, 1);
1769     }
1770     ''', name: '__cpuid',
1771     args: test_c_args)
1772   cdata.set('HAVE__CPUID', 1)
1773 endif
1776 # Defend against clang being used on x86-32 without SSE2 enabled.  As current
1777 # versions of clang do not understand -fexcess-precision=standard, the use of
1778 # x87 floating point operations leads to problems like isinf possibly returning
1779 # false for a value that is infinite when converted from the 80bit register to
1780 # the 8byte memory representation.
1782 # Only perform the test if the compiler doesn't understand
1783 # -fexcess-precision=standard, that way a potentially fixed compiler will work
1784 # automatically.
1785 if '-fexcess-precision=standard' not in cflags
1786   if not cc.compiles('''
1787 #if defined(__clang__) && defined(__i386__) && !defined(__SSE2_MATH__)
1788 choke me
1789 #endif''',
1790       name: '', args: test_c_args)
1791     error('Compiling PostgreSQL with clang, on 32bit x86, requires SSE2 support. Use -msse2 or use gcc.')
1792   endif
1793 endif
1797 ###############################################################
1798 # Compiler flags
1799 ###############################################################
1801 common_functional_flags = [
1802   # Disable strict-aliasing rules; needed for gcc 3.3+
1803   '-fno-strict-aliasing',
1804   # Disable optimizations that assume no overflow; needed for gcc 4.3+
1805   '-fwrapv',
1806   '-fexcess-precision=standard',
1809 cflags += cc.get_supported_arguments(common_functional_flags)
1810 if llvm.found()
1811   cxxflags += cpp.get_supported_arguments(common_functional_flags)
1812 endif
1814 vectorize_cflags = cc.get_supported_arguments(['-ftree-vectorize'])
1815 unroll_loops_cflags = cc.get_supported_arguments(['-funroll-loops'])
1817 common_warning_flags = [
1818   '-Wmissing-prototypes',
1819   '-Wpointer-arith',
1820   # Really don't want VLAs to be used in our dialect of C
1821   '-Werror=vla',
1822   # On macOS, complain about usage of symbols newer than the deployment target
1823   '-Werror=unguarded-availability-new',
1824   '-Wendif-labels',
1825   '-Wmissing-format-attribute',
1826   '-Wimplicit-fallthrough=3',
1827   '-Wcast-function-type',
1828   '-Wshadow=compatible-local',
1829   # This was included in -Wall/-Wformat in older GCC versions
1830   '-Wformat-security',
1833 cflags_warn += cc.get_supported_arguments(common_warning_flags)
1834 if llvm.found()
1835   cxxflags_warn += cpp.get_supported_arguments(common_warning_flags)
1836 endif
1838 # A few places with imported code get a pass on -Wdeclaration-after-statement, remember
1839 # the result for them
1840 cflags_no_decl_after_statement = []
1841 if cc.has_argument('-Wdeclaration-after-statement')
1842   cflags_warn += '-Wdeclaration-after-statement'
1843   cflags_no_decl_after_statement += '-Wno-declaration-after-statement'
1844 endif
1847 # The following tests want to suppress various unhelpful warnings by adding
1848 # -Wno-foo switches.  But gcc won't complain about unrecognized -Wno-foo
1849 # switches, so we have to test for the positive form and if that works,
1850 # add the negative form.
1852 negative_warning_flags = [
1853   # Suppress clang's unhelpful unused-command-line-argument warnings.
1854   'unused-command-line-argument',
1856   # Remove clang 12+'s compound-token-split-by-macro, as this causes a lot
1857   # of warnings when building plperl because of usages in the Perl headers.
1858   'compound-token-split-by-macro',
1860   # Similarly disable useless truncation warnings from gcc 8+
1861   'format-truncation',
1862   'stringop-truncation',
1864   # Suppress clang 16's strict warnings about function casts
1865   'cast-function-type-strict',
1867   # To make warning_level=2 / -Wextra work, we'd need at least the following
1868   # 'clobbered',
1869   # 'missing-field-initializers',
1870   # 'sign-compare',
1871   # 'unused-parameter',
1874 foreach w : negative_warning_flags
1875   if cc.has_argument('-W' + w)
1876     cflags_warn += '-Wno-' + w
1877   endif
1878   if llvm.found() and cpp.has_argument('-W' + w)
1879     cxxflags_warn += '-Wno-' + w
1880   endif
1881 endforeach
1884 if cc.get_id() == 'msvc'
1885   cflags_warn += [
1886     '/wd4018', # signed/unsigned mismatch
1887     '/wd4244', # conversion from 'type1' to 'type2', possible loss of data
1888     '/wd4273', # inconsistent DLL linkage
1889     '/wd4101', # unreferenced local variable
1890     '/wd4102', # unreferenced label
1891     '/wd4090', # different 'modifier' qualifiers
1892     '/wd4267', # conversion from 'size_t' to 'type', possible loss of data
1893   ]
1895   cppflags += [
1896     '/DWIN32',
1897     '/DWINDOWS',
1898     '/D__WINDOWS__',
1899     '/D__WIN32__',
1900     '/D_CRT_SECURE_NO_DEPRECATE',
1901     '/D_CRT_NONSTDC_NO_DEPRECATE',
1902   ]
1904   # We never need export libraries. As link.exe reports their creation, they
1905   # are unnecessarily noisy. Similarly, we don't need import library for
1906   # modules, we only import them dynamically, and they're also noisy.
1907   ldflags += '/NOEXP'
1908   ldflags_mod += '/NOIMPLIB'
1909 endif
1913 ###############################################################
1914 # Atomics
1915 ###############################################################
1917 if not get_option('spinlocks')
1918   warning('Not using spinlocks will cause poor performance')
1919 else
1920   cdata.set('HAVE_SPINLOCKS', 1)
1921 endif
1923 if not get_option('atomics')
1924   warning('Not using atomics will cause poor performance')
1925 else
1926   # XXX: perhaps we should require some atomics support in this case these
1927   # days?
1928   cdata.set('HAVE_ATOMICS', 1)
1930   atomic_checks = [
1931     {'name': 'HAVE_GCC__SYNC_CHAR_TAS',
1932      'desc': '__sync_lock_test_and_set(char)',
1933      'test': '''
1934 char lock = 0;
1935 __sync_lock_test_and_set(&lock, 1);
1936 __sync_lock_release(&lock);'''},
1938     {'name': 'HAVE_GCC__SYNC_INT32_TAS',
1939      'desc': '__sync_lock_test_and_set(int32)',
1940      'test': '''
1941 int lock = 0;
1942 __sync_lock_test_and_set(&lock, 1);
1943 __sync_lock_release(&lock);'''},
1945     {'name': 'HAVE_GCC__SYNC_INT32_CAS',
1946      'desc': '__sync_val_compare_and_swap(int32)',
1947      'test': '''
1948 int val = 0;
1949 __sync_val_compare_and_swap(&val, 0, 37);'''},
1951     {'name': 'HAVE_GCC__SYNC_INT64_CAS',
1952      'desc': '__sync_val_compare_and_swap(int64)',
1953      'test': '''
1954 INT64 val = 0;
1955 __sync_val_compare_and_swap(&val, 0, 37);'''},
1957     {'name': 'HAVE_GCC__ATOMIC_INT32_CAS',
1958      'desc': ' __atomic_compare_exchange_n(int32)',
1959      'test': '''
1960 int val = 0;
1961 int expect = 0;
1962 __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);'''},
1964     {'name': 'HAVE_GCC__ATOMIC_INT64_CAS',
1965      'desc': ' __atomic_compare_exchange_n(int64)',
1966      'test': '''
1967 INT64 val = 0;
1968 INT64 expect = 0;
1969 __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);'''},
1970   ]
1972   foreach check : atomic_checks
1973     test = '''
1974 int main(void)
1977 }'''.format(check['test'])
1979     cdata.set(check['name'],
1980       cc.links(test,
1981         name: check['desc'],
1982         args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))]) ? 1 : false
1983     )
1984   endforeach
1986 endif
1990 ###############################################################
1991 # Select CRC-32C implementation.
1993 # If we are targeting a processor that has Intel SSE 4.2 instructions, we can
1994 # use the special CRC instructions for calculating CRC-32C. If we're not
1995 # targeting such a processor, but we can nevertheless produce code that uses
1996 # the SSE intrinsics, perhaps with some extra CFLAGS, compile both
1997 # implementations and select which one to use at runtime, depending on whether
1998 # SSE 4.2 is supported by the processor we're running on.
2000 # Similarly, if we are targeting an ARM processor that has the CRC
2001 # instructions that are part of the ARMv8 CRC Extension, use them. And if
2002 # we're not targeting such a processor, but can nevertheless produce code that
2003 # uses the CRC instructions, compile both, and select at runtime.
2004 ###############################################################
2006 have_optimized_crc = false
2007 cflags_crc = []
2008 if host_cpu == 'x86' or host_cpu == 'x86_64'
2010   if cc.get_id() == 'msvc'
2011     cdata.set('USE_SSE42_CRC32C', false)
2012     cdata.set('USE_SSE42_CRC32C_WITH_RUNTIME_CHECK', 1)
2013     have_optimized_crc = true
2014   else
2016     prog = '''
2017 #include <nmmintrin.h>
2019 int main(void)
2021     unsigned int crc = 0;
2022     crc = _mm_crc32_u8(crc, 0);
2023     crc = _mm_crc32_u32(crc, 0);
2024     /* return computed value, to prevent the above being optimized away */
2025     return crc == 0;
2029     if cc.links(prog, name: '_mm_crc32_u8 and _mm_crc32_u32 without -msse4.2',
2030           args: test_c_args)
2031       # Use Intel SSE 4.2 unconditionally.
2032       cdata.set('USE_SSE42_CRC32C', 1)
2033       have_optimized_crc = true
2034     elif cc.links(prog, name: '_mm_crc32_u8 and _mm_crc32_u32 with -msse4.2',
2035           args: test_c_args + ['-msse4.2'])
2036       # Use Intel SSE 4.2, with runtime check. The CPUID instruction is needed for
2037       # the runtime check.
2038       cflags_crc += '-msse4.2'
2039       cdata.set('USE_SSE42_CRC32C', false)
2040       cdata.set('USE_SSE42_CRC32C_WITH_RUNTIME_CHECK', 1)
2041       have_optimized_crc = true
2042     endif
2044   endif
2046 elif host_cpu == 'arm' or host_cpu == 'aarch64'
2048   prog = '''
2049 #include <arm_acle.h>
2051 int main(void)
2053     unsigned int crc = 0;
2054     crc = __crc32cb(crc, 0);
2055     crc = __crc32ch(crc, 0);
2056     crc = __crc32cw(crc, 0);
2057     crc = __crc32cd(crc, 0);
2059     /* return computed value, to prevent the above being optimized away */
2060     return crc == 0;
2064   if cc.links(prog, name: '__crc32cb, __crc32ch, __crc32cw, and __crc32cd without -march=armv8-a+crc',
2065       args: test_c_args)
2066     # Use ARM CRC Extension unconditionally
2067     cdata.set('USE_ARMV8_CRC32C', 1)
2068     have_optimized_crc = true
2069   elif cc.links(prog, name: '__crc32cb, __crc32ch, __crc32cw, and __crc32cd with -march=armv8-a+crc',
2070       args: test_c_args + ['-march=armv8-a+crc'])
2071     # Use ARM CRC Extension, with runtime check
2072     cflags_crc += '-march=armv8-a+crc'
2073     cdata.set('USE_ARMV8_CRC32C', false)
2074     cdata.set('USE_ARMV8_CRC32C_WITH_RUNTIME_CHECK', 1)
2075     have_optimized_crc = true
2076   endif
2078 elif host_cpu == 'loongarch64'
2080   prog = '''
2081 int main(void)
2083     unsigned int crc = 0;
2084     crc = __builtin_loongarch_crcc_w_b_w(0, crc);
2085     crc = __builtin_loongarch_crcc_w_h_w(0, crc);
2086     crc = __builtin_loongarch_crcc_w_w_w(0, crc);
2087     crc = __builtin_loongarch_crcc_w_d_w(0, crc);
2089     /* return computed value, to prevent the above being optimized away */
2090     return crc == 0;
2094   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',
2095       args: test_c_args)
2096     # Use LoongArch CRC instruction unconditionally
2097     cdata.set('USE_LOONGARCH_CRC32C', 1)
2098     have_optimized_crc = true
2099   endif
2101 endif
2103 if not have_optimized_crc
2104   # fall back to slicing-by-8 algorithm, which doesn't require any special CPU
2105   # support.
2106   cdata.set('USE_SLICING_BY_8_CRC32C', 1)
2107 endif
2111 ###############################################################
2112 # Other CPU specific stuff
2113 ###############################################################
2115 if host_cpu == 'x86_64'
2117   if cc.compiles('''
2118       void main(void)
2119       {
2120           long long x = 1; long long r;
2121           __asm__ __volatile__ (" popcntq %1,%0\n" : "=q"(r) : "rm"(x));
2122       }''',
2123       name: '@0@: popcntq instruction'.format(host_cpu),
2124       args: test_c_args)
2125     cdata.set('HAVE_X86_64_POPCNTQ', 1)
2126   endif
2128 elif host_cpu == 'ppc' or host_cpu == 'ppc64'
2129   # Check if compiler accepts "i"(x) when __builtin_constant_p(x).
2130   if cdata.has('HAVE__BUILTIN_CONSTANT_P')
2131     if cc.compiles('''
2132       static inline int
2133       addi(int ra, int si)
2134       {
2135           int res = 0;
2136           if (__builtin_constant_p(si))
2137               __asm__ __volatile__(
2138                   " addi %0,%1,%2\n" : "=r"(res) : "b"(ra), "i"(si));
2139           return res;
2140       }
2141       int test_adds(int x) { return addi(3, x) + addi(x, 5); }
2142       ''',
2143       args: test_c_args)
2144       cdata.set('HAVE_I_CONSTRAINT__BUILTIN_CONSTANT_P', 1)
2145     endif
2146   endif
2147 endif
2151 ###############################################################
2152 # Library / OS tests
2153 ###############################################################
2155 # XXX: Might be worth conditioning some checks on the OS, to avoid doing
2156 # unnecessary checks over and over, particularly on windows.
2157 header_checks = [
2158   'atomic.h',
2159   'copyfile.h',
2160   'crtdefs.h',
2161   'execinfo.h',
2162   'getopt.h',
2163   'ifaddrs.h',
2164   'langinfo.h',
2165   'mbarrier.h',
2166   'stdbool.h',
2167   'strings.h',
2168   'sys/epoll.h',
2169   'sys/event.h',
2170   'sys/personality.h',
2171   'sys/prctl.h',
2172   'sys/procctl.h',
2173   'sys/signalfd.h',
2174   'sys/ucred.h',
2175   'termios.h',
2176   'ucred.h',
2179 foreach header : header_checks
2180   varname = 'HAVE_' + header.underscorify().to_upper()
2182   # Emulate autoconf behaviour of not-found->undef, found->1
2183   found = cc.has_header(header,
2184     include_directories: postgres_inc, args: test_c_args)
2185   cdata.set(varname, found ? 1 : false,
2186             description: 'Define to 1 if you have the <@0@> header file.'.format(header))
2187 endforeach
2190 decl_checks = [
2191   ['F_FULLFSYNC', 'fcntl.h'],
2192   ['fdatasync', 'unistd.h'],
2193   ['posix_fadvise', 'fcntl.h'],
2194   ['strlcat', 'string.h'],
2195   ['strlcpy', 'string.h'],
2196   ['strnlen', 'string.h'],
2199 # Need to check for function declarations for these functions, because
2200 # checking for library symbols wouldn't handle deployment target
2201 # restrictions on macOS
2202 decl_checks += [
2203   ['preadv', 'sys/uio.h'],
2204   ['pwritev', 'sys/uio.h'],
2207 foreach c : decl_checks
2208   func = c.get(0)
2209   header = c.get(1)
2210   args = c.get(2, {})
2211   varname = 'HAVE_DECL_' + func.underscorify().to_upper()
2213   found = cc.has_header_symbol(header, func,
2214     args: test_c_args, include_directories: postgres_inc,
2215     kwargs: args)
2216   cdata.set10(varname, found, description:
2217 '''Define to 1 if you have the declaration of `@0@', and to 0 if you
2218    don't.'''.format(func))
2219 endforeach
2222 if cc.has_type('struct option',
2223     args: test_c_args, include_directories: postgres_inc,
2224     prefix: '@0@'.format(cdata.get('HAVE_GETOPT_H')) == '1' ? '#include <getopt.h>' : '')
2225   cdata.set('HAVE_STRUCT_OPTION', 1)
2226 endif
2229 foreach c : ['opterr', 'optreset']
2230   varname = 'HAVE_INT_' + c.underscorify().to_upper()
2232   if cc.links('''
2233 #include <unistd.h>
2234 int main(void)
2236     extern int @0@;
2237     @0@ = 1;
2239 '''.format(c), name: c, args: test_c_args)
2240     cdata.set(varname, 1)
2241   else
2242     cdata.set(varname, false)
2243   endif
2244 endforeach
2246 if cc.has_type('socklen_t',
2247     args: test_c_args, include_directories: postgres_inc,
2248     prefix: '''
2249 #include <sys/socket.h>''')
2250   cdata.set('HAVE_SOCKLEN_T', 1)
2251 endif
2253 if cc.has_member('struct sockaddr', 'sa_len',
2254     args: test_c_args, include_directories: postgres_inc,
2255     prefix: '''
2256 #include <sys/types.h>
2257 #include <sys/socket.h>''')
2258   cdata.set('HAVE_STRUCT_SOCKADDR_SA_LEN', 1)
2259 endif
2261 if cc.has_member('struct tm', 'tm_zone',
2262     args: test_c_args, include_directories: postgres_inc,
2263     prefix: '''
2264 #include <sys/types.h>
2265 #include <time.h>
2266 ''')
2267   cdata.set('HAVE_STRUCT_TM_TM_ZONE', 1)
2268 endif
2270 if cc.compiles('''
2271 #include <time.h>
2272 extern int foo(void);
2273 int foo(void)
2275     return timezone / 60;
2277 ''',
2278     name: 'global variable `timezone\' exists',
2279     args: test_c_args, include_directories: postgres_inc)
2280   cdata.set('HAVE_INT_TIMEZONE', 1)
2281 else
2282   cdata.set('HAVE_INT_TIMEZONE', false)
2283 endif
2285 if cc.has_type('union semun',
2286     args: test_c_args,
2287     include_directories: postgres_inc,
2288     prefix: '''
2289 #include <sys/types.h>
2290 #include <sys/ipc.h>
2291 #include <sys/sem.h>
2292 ''')
2293   cdata.set('HAVE_UNION_SEMUN', 1)
2294 endif
2296 if cc.compiles('''
2297 #include <string.h>
2298 int main(void)
2300   char buf[100];
2301   switch (strerror_r(1, buf, sizeof(buf)))
2302   { case 0: break; default: break; }
2303 }''',
2304     name: 'strerror_r',
2305     args: test_c_args, include_directories: postgres_inc)
2306   cdata.set('STRERROR_R_INT', 1)
2307 else
2308   cdata.set('STRERROR_R_INT', false)
2309 endif
2311 # Find the right header file for the locale_t type.  macOS needs xlocale.h;
2312 # standard is locale.h, but glibc <= 2.25 also had an xlocale.h file that
2313 # we should not use so we check the standard header first.  MSVC has a
2314 # replacement defined in src/include/port/win32_port.h.
2315 if not cc.has_type('locale_t', prefix: '#include <locale.h>') and \
2316    cc.has_type('locale_t', prefix: '#include <xlocale.h>')
2317   cdata.set('LOCALE_T_IN_XLOCALE', 1)
2318 endif
2320 # Check if the C compiler understands typeof or a variant.  Define
2321 # HAVE_TYPEOF if so, and define 'typeof' to the actual key word.
2322 foreach kw : ['typeof', '__typeof__', 'decltype']
2323   if cc.compiles('''
2324 int main(void)
2326     int x = 0;
2327     @0@(x) y;
2328     y = x;
2329     return y;
2331 '''.format(kw),
2332     name: 'typeof()',
2333     args: test_c_args, include_directories: postgres_inc)
2335     cdata.set('HAVE_TYPEOF', 1)
2336     if kw != 'typeof'
2337       cdata.set('typeof', kw)
2338     endif
2340     break
2341   endif
2342 endforeach
2345 # Try to find a declaration for wcstombs_l().  It might be in stdlib.h
2346 # (following the POSIX requirement for wcstombs()), or in locale.h, or in
2347 # xlocale.h.  If it's in the latter, define WCSTOMBS_L_IN_XLOCALE.
2348 wcstombs_l_test = '''
2349 #include <stdlib.h>
2350 #include <locale.h>
2353 void main(void)
2355 #ifndef wcstombs_l
2356     (void) wcstombs_l;
2357 #endif
2360 if (not cc.compiles(wcstombs_l_test.format(''),
2361       name: 'wcstombs_l') and
2362     cc.compiles(wcstombs_l_test.format('#include <xlocale.h>'),
2363       name: 'wcstombs_l in xlocale.h'))
2364     cdata.set('WCSTOMBS_L_IN_XLOCALE', 1)
2365 endif
2368 # MSVC doesn't cope well with defining restrict to __restrict, the spelling it
2369 # understands, because it conflicts with __declspec(restrict). Therefore we
2370 # define pg_restrict to the appropriate definition, which presumably won't
2371 # conflict.
2373 # We assume C99 support, so we don't need to make this conditional.
2375 # XXX: Historically we allowed platforms to disable restrict in template
2376 # files, but that was only added for AIX when building with XLC, which we
2377 # don't support yet.
2378 cdata.set('pg_restrict', '__restrict')
2381 # Most libraries are included only if they demonstrably provide a function we
2382 # need, but libm is an exception: always include it, because there are too
2383 # many compilers that play cute optimization games that will break probes for
2384 # standard functions such as pow().
2385 os_deps += cc.find_library('m', required: false)
2387 rt_dep = cc.find_library('rt', required: false)
2389 dl_dep = cc.find_library('dl', required: false)
2391 util_dep = cc.find_library('util', required: false)
2393 getopt_dep = cc.find_library('getopt', required: false)
2394 gnugetopt_dep = cc.find_library('gnugetopt', required: false)
2395 # Check if we want to replace getopt/getopt_long even if provided by the system
2396 # - Mingw has adopted a GNU-centric interpretation of optind/optreset,
2397 #   so always use our version on Windows
2398 # - On OpenBSD and Solaris, getopt() doesn't do what we want for long options
2399 #   (i.e., allow '-' as a flag character), so use our version on those platforms
2400 # - We want to use system's getopt_long() only if the system provides struct
2401 #   option
2402 always_replace_getopt = host_system in ['windows', 'cygwin', 'openbsd', 'solaris']
2403 always_replace_getopt_long = host_system in ['windows', 'cygwin'] or not cdata.has('HAVE_STRUCT_OPTION')
2405 # Required on BSDs
2406 execinfo_dep = cc.find_library('execinfo', required: false)
2408 if host_system == 'cygwin'
2409   cygipc_dep = cc.find_library('cygipc', required: false)
2410 else
2411   cygipc_dep = not_found_dep
2412 endif
2414 if host_system == 'sunos'
2415   socket_dep = cc.find_library('socket', required: false)
2416 else
2417   socket_dep = not_found_dep
2418 endif
2420 # XXX: Might be worth conditioning some checks on the OS, to avoid doing
2421 # unnecessary checks over and over, particularly on windows.
2422 func_checks = [
2423   ['_configthreadlocale', {'skip': host_system != 'windows'}],
2424   ['backtrace_symbols', {'dependencies': [execinfo_dep]}],
2425   ['clock_gettime', {'dependencies': [rt_dep], 'define': false}],
2426   ['copyfile'],
2427   # gcc/clang's sanitizer helper library provides dlopen but not dlsym, thus
2428   # when enabling asan the dlopen check doesn't notice that -ldl is actually
2429   # required. Just checking for dlsym() ought to suffice.
2430   ['dlsym', {'dependencies': [dl_dep], 'define': false}],
2431   ['explicit_bzero'],
2432   ['getifaddrs'],
2433   ['getopt', {'dependencies': [getopt_dep, gnugetopt_dep], 'skip': always_replace_getopt}],
2434   ['getopt_long', {'dependencies': [getopt_dep, gnugetopt_dep], 'skip': always_replace_getopt_long}],
2435   ['getpeereid'],
2436   ['getpeerucred'],
2437   ['inet_aton'],
2438   ['inet_pton'],
2439   ['kqueue'],
2440   ['mbstowcs_l'],
2441   ['memset_s'],
2442   ['mkdtemp'],
2443   ['posix_fadvise'],
2444   ['posix_fallocate'],
2445   ['ppoll'],
2446   ['pstat'],
2447   ['pthread_barrier_wait', {'dependencies': [thread_dep]}],
2448   ['pthread_is_threaded_np', {'dependencies': [thread_dep]}],
2449   ['sem_init', {'dependencies': [rt_dep, thread_dep], 'skip': sema_kind != 'unnamed_posix', 'define': false}],
2450   ['setproctitle', {'dependencies': [util_dep]}],
2451   ['setproctitle_fast'],
2452   ['shm_open', {'dependencies': [rt_dep], 'define': false}],
2453   ['shm_unlink', {'dependencies': [rt_dep], 'define': false}],
2454   ['shmget', {'dependencies': [cygipc_dep], 'define': false}],
2455   ['socket', {'dependencies': [socket_dep], 'define': false}],
2456   ['strchrnul'],
2457   ['strerror_r', {'dependencies': [thread_dep]}],
2458   ['strlcat'],
2459   ['strlcpy'],
2460   ['strnlen'],
2461   ['strsignal'],
2462   ['sync_file_range'],
2463   ['syncfs'],
2464   ['uselocale'],
2465   ['wcstombs_l'],
2468 func_check_results = {}
2469 foreach c : func_checks
2470   func = c.get(0)
2471   kwargs = c.get(1, {})
2472   deps = kwargs.get('dependencies', [])
2474   if kwargs.get('skip', false)
2475     continue
2476   endif
2478   found = cc.has_function(func, args: test_c_args)
2480   if not found
2481     foreach dep : deps
2482       if not dep.found()
2483         continue
2484       endif
2485       found = cc.has_function(func, args: test_c_args,
2486                               dependencies: [dep])
2487       if found
2488         os_deps += dep
2489         break
2490       endif
2491     endforeach
2492   endif
2494   func_check_results += {func: found}
2496   if kwargs.get('define', true)
2497     # Emulate autoconf behaviour of not-found->undef, found->1
2498     cdata.set('HAVE_' + func.underscorify().to_upper(),
2499               found  ? 1 : false,
2500               description: 'Define to 1 if you have the `@0@\' function.'.format(func))
2501   endif
2502 endforeach
2505 if cc.has_function('syslog', args: test_c_args) and \
2506     cc.check_header('syslog.h', args: test_c_args)
2507   cdata.set('HAVE_SYSLOG', 1)
2508 endif
2511 # if prerequisites for unnamed posix semas aren't fulfilled, fall back to sysv
2512 # semaphores
2513 if sema_kind == 'unnamed_posix' and \
2514    not func_check_results.get('sem_init', false)
2515   sema_kind = 'sysv'
2516 endif
2518 cdata.set('USE_@0@_SHARED_MEMORY'.format(shmem_kind.to_upper()), 1)
2519 cdata.set('USE_@0@_SEMAPHORES'.format(sema_kind.to_upper()), 1)
2521 cdata.set('MEMSET_LOOP_LIMIT', memset_loop_limit)
2522 cdata.set_quoted('DLSUFFIX', dlsuffix)
2525 # built later than the rest of the version metadata, we need SIZEOF_VOID_P
2526 cdata.set_quoted('PG_VERSION_STR',
2527   'PostgreSQL @0@ on @1@-@2@, compiled by @3@-@4@, @5@-bit'.format(
2528     pg_version, host_machine.cpu_family(), host_system,
2529     cc.get_id(), cc.version(), cdata.get('SIZEOF_VOID_P') * 8,
2530   )
2534 ###############################################################
2535 # NLS / Gettext
2536 ###############################################################
2538 nlsopt = get_option('nls')
2539 libintl = not_found_dep
2541 if not nlsopt.disabled()
2542   # otherwise there'd be lots of
2543   # "Gettext not found, all translation (po) targets will be ignored."
2544   # warnings if not found.
2545   msgfmt = find_program('msgfmt', required: nlsopt, native: true)
2547   # meson 0.59 has this wrapped in dependency('intl')
2548   if (msgfmt.found() and
2549       cc.check_header('libintl.h', required: nlsopt,
2550         args: test_c_args, include_directories: postgres_inc))
2552     # in libc
2553     if cc.has_function('ngettext')
2554       libintl = declare_dependency()
2555     else
2556       libintl = cc.find_library('intl',
2557         has_headers: ['libintl.h'], required: nlsopt,
2558         header_include_directories: postgres_inc,
2559         dirs: test_lib_d)
2560     endif
2561   endif
2563   if libintl.found()
2564     i18n = import('i18n')
2565     cdata.set('ENABLE_NLS', 1)
2566   endif
2567 endif
2571 ###############################################################
2572 # Build
2573 ###############################################################
2575 # Set up compiler / linker arguments to be used everywhere, individual targets
2576 # can add further args directly, or indirectly via dependencies
2577 add_project_arguments(cflags, language: ['c'])
2578 add_project_arguments(cppflags, language: ['c'])
2579 add_project_arguments(cflags_warn, language: ['c'])
2580 add_project_arguments(cxxflags, language: ['cpp'])
2581 add_project_arguments(cppflags, language: ['cpp'])
2582 add_project_arguments(cxxflags_warn, language: ['cpp'])
2583 add_project_link_arguments(ldflags, language: ['c', 'cpp'])
2586 # Collect a number of lists of things while recursing through the source
2587 # tree. Later steps then can use those.
2589 # list of targets for various alias targets
2590 backend_targets = []
2591 bin_targets = []
2592 pl_targets = []
2593 contrib_targets = []
2594 testprep_targets = []
2595 nls_targets = []
2598 # Define the tests to distribute them to the correct test styles later
2599 test_deps = []
2600 tests = []
2603 # Default options for targets
2605 # First identify rpaths
2606 bin_install_rpaths = []
2607 lib_install_rpaths = []
2608 mod_install_rpaths = []
2611 # Don't add rpaths on darwin for now - as long as only absolute references to
2612 # libraries are needed, absolute LC_ID_DYLIB ensures libraries can be found in
2613 # their final destination.
2614 if host_system != 'darwin'
2615   # Add absolute path to libdir to rpath. This ensures installed binaries /
2616   # libraries find our libraries (mainly libpq).
2617   bin_install_rpaths += dir_prefix / dir_lib
2618   lib_install_rpaths += dir_prefix / dir_lib
2619   mod_install_rpaths += dir_prefix / dir_lib
2621   # Add extra_lib_dirs to rpath. This ensures we find libraries we depend on.
2622   #
2623   # Not needed on darwin even if we use relative rpaths for our own libraries,
2624   # as the install_name of libraries in extra_lib_dirs will point to their
2625   # location anyway.
2626   bin_install_rpaths += postgres_lib_d
2627   lib_install_rpaths += postgres_lib_d
2628   mod_install_rpaths += postgres_lib_d
2629 endif
2632 # Define arguments for default targets
2634 default_target_args = {
2635   'implicit_include_directories': false,
2636   'install': true,
2639 default_lib_args = default_target_args + {
2640   'name_prefix': '',
2643 internal_lib_args = default_lib_args + {
2644   'build_by_default': false,
2645   'install': false,
2648 default_mod_args = default_lib_args + {
2649   'name_prefix': '',
2650   'install_dir': dir_lib_pkg,
2653 default_bin_args = default_target_args + {
2654   'install_dir': dir_bin,
2657 if get_option('rpath')
2658   default_lib_args += {
2659     'install_rpath': ':'.join(lib_install_rpaths),
2660   }
2662   default_mod_args += {
2663     'install_rpath': ':'.join(mod_install_rpaths),
2664   }
2666   default_bin_args += {
2667     'install_rpath': ':'.join(bin_install_rpaths),
2668   }
2669 endif
2672 # Helper for exporting a limited number of symbols
2673 gen_export_kwargs = {
2674   'input': 'exports.txt',
2675   'output': '@BASENAME@.'+export_file_suffix,
2676   'command': [perl, files('src/tools/gen_export.pl'),
2677    '--format', export_file_format,
2678    '--input', '@INPUT0@', '--output', '@OUTPUT0@'],
2679   'build_by_default': false,
2680   'install': false,
2686 ### Helpers for custom targets used across the tree
2689 catalog_pm = files('src/backend/catalog/Catalog.pm')
2690 perfect_hash_pm = files('src/tools/PerfectHash.pm')
2691 gen_kwlist_deps = [perfect_hash_pm]
2692 gen_kwlist_cmd = [
2693   perl, '-I', '@SOURCE_ROOT@/src/tools',
2694   files('src/tools/gen_keywordlist.pl'),
2695   '--output', '@OUTDIR@', '@INPUT@']
2700 ### windows resources related stuff
2703 if host_system == 'windows'
2704   pg_ico = meson.source_root() / 'src' / 'port' / 'win32.ico'
2705   win32ver_rc = files('src/port/win32ver.rc')
2706   rcgen = find_program('src/tools/rcgen', native: true)
2708   rcgen_base_args = [
2709     '--srcdir', '@SOURCE_DIR@',
2710     '--builddir', meson.build_root(),
2711     '--rcout', '@OUTPUT0@',
2712     '--out', '@OUTPUT1@',
2713     '--input', '@INPUT@',
2714     '@EXTRA_ARGS@',
2715   ]
2717   if cc.get_argument_syntax() == 'msvc'
2718     rc = find_program('rc', required: true)
2719     rcgen_base_args += ['--rc', rc.path()]
2720     rcgen_outputs = ['@BASENAME@.rc', '@BASENAME@.res']
2721   else
2722     windres = find_program('windres', required: true)
2723     rcgen_base_args += ['--windres', windres.path()]
2724     rcgen_outputs = ['@BASENAME@.rc', '@BASENAME@.obj']
2725   endif
2727   # msbuild backend doesn't support this atm
2728   if meson.backend() == 'ninja'
2729     rcgen_base_args += ['--depfile', '@DEPFILE@']
2730   endif
2732   rcgen_bin_args = rcgen_base_args + [
2733     '--VFT_TYPE', 'VFT_APP',
2734     '--FILEENDING', 'exe',
2735     '--ICO', pg_ico
2736   ]
2738   rcgen_lib_args = rcgen_base_args + [
2739     '--VFT_TYPE', 'VFT_DLL',
2740     '--FILEENDING', 'dll',
2741   ]
2743   rc_bin_gen = generator(rcgen,
2744     depfile: '@BASENAME@.d',
2745     arguments: rcgen_bin_args,
2746     output: rcgen_outputs,
2747   )
2749   rc_lib_gen = generator(rcgen,
2750     depfile: '@BASENAME@.d',
2751     arguments: rcgen_lib_args,
2752     output: rcgen_outputs,
2753   )
2754 endif
2758 # headers that the whole build tree depends on
2759 generated_headers = []
2760 # headers that the backend build depends on
2761 generated_backend_headers = []
2762 # configure_files() output, needs a way of converting to file names
2763 configure_files = []
2765 # generated files that might conflict with a partial in-tree autoconf build
2766 generated_sources = []
2767 # same, for paths that differ between autoconf / meson builds
2768 # elements are [dir, [files]]
2769 generated_sources_ac = {}
2772 # First visit src/include - all targets creating headers are defined
2773 # within. That makes it easy to add the necessary dependencies for the
2774 # subsequent build steps.
2776 subdir('src/include')
2778 subdir('config')
2780 # Then through src/port and src/common, as most other things depend on them
2782 frontend_port_code = declare_dependency(
2783   compile_args: ['-DFRONTEND'],
2784   include_directories: [postgres_inc],
2785   dependencies: os_deps,
2788 backend_port_code = declare_dependency(
2789   compile_args: ['-DBUILDING_DLL'],
2790   include_directories: [postgres_inc],
2791   sources: [errcodes], # errcodes.h is needed due to use of ereport
2792   dependencies: os_deps,
2795 subdir('src/port')
2797 frontend_common_code = declare_dependency(
2798   compile_args: ['-DFRONTEND'],
2799   include_directories: [postgres_inc],
2800   sources: generated_headers,
2801   dependencies: [os_deps, zlib, zstd],
2804 backend_common_code = declare_dependency(
2805   compile_args: ['-DBUILDING_DLL'],
2806   include_directories: [postgres_inc],
2807   sources: generated_headers,
2808   dependencies: [os_deps, zlib, zstd],
2811 subdir('src/common')
2813 # all shared libraries should depend on shlib_code
2814 shlib_code = declare_dependency(
2815   link_args: ldflags_sl,
2818 # all static libraries not part of the backend should depend on this
2819 frontend_stlib_code = declare_dependency(
2820   include_directories: [postgres_inc],
2821   link_with: [common_static, pgport_static],
2822   sources: generated_headers,
2823   dependencies: [os_deps, libintl],
2826 # all shared libraries not part of the backend should depend on this
2827 frontend_shlib_code = declare_dependency(
2828   include_directories: [postgres_inc],
2829   link_with: [common_shlib, pgport_shlib],
2830   sources: generated_headers,
2831   dependencies: [shlib_code, os_deps, libintl],
2834 # Dependencies both for static and shared libpq
2835 libpq_deps += [
2836   thread_dep,
2838   gssapi,
2839   ldap_r,
2840   libintl,
2841   ssl,
2844 subdir('src/interfaces/libpq')
2845 # fe_utils depends on libpq
2846 subdir('src/fe_utils')
2848 # for frontend binaries
2849 frontend_code = declare_dependency(
2850   include_directories: [postgres_inc],
2851   link_with: [fe_utils, common_static, pgport_static],
2852   sources: generated_headers,
2853   dependencies: [os_deps, libintl],
2856 backend_both_deps += [
2857   thread_dep,
2858   bsd_auth,
2859   gssapi,
2860   icu,
2861   icu_i18n,
2862   ldap,
2863   libintl,
2864   libxml,
2865   lz4,
2866   pam,
2867   ssl,
2868   systemd,
2869   zlib,
2870   zstd,
2873 backend_mod_deps = backend_both_deps + os_deps
2875 backend_code = declare_dependency(
2876   compile_args: ['-DBUILDING_DLL'],
2877   include_directories: [postgres_inc],
2878   link_args: ldflags_be,
2879   link_with: [],
2880   sources: generated_headers + generated_backend_headers,
2881   dependencies: os_deps + backend_both_deps + backend_deps,
2884 # install these files only during test, not main install
2885 test_install_data = []
2886 test_install_libs = []
2888 # src/backend/meson.build defines backend_mod_code used for extension
2889 # libraries.
2892 # Then through the main sources. That way contrib can have dependencies on
2893 # main sources. Note that this explicitly doesn't enter src/test, right now a
2894 # few regression tests depend on contrib files.
2896 subdir('src')
2898 subdir('contrib')
2900 subdir('src/test')
2901 subdir('src/interfaces/libpq/test')
2902 subdir('src/interfaces/ecpg/test')
2904 subdir('doc/src/sgml')
2906 generated_sources_ac += {'': ['GNUmakefile']}
2908 # After processing src/test, add test_install_libs to the testprep_targets
2909 # to build them
2910 testprep_targets += test_install_libs
2913 # If there are any files in the source directory that we also generate in the
2914 # build directory, they might get preferred over the newly generated files,
2915 # e.g. because of a #include "file", which always will search in the current
2916 # directory first.
2917 message('checking for file conflicts between source and build directory')
2918 conflicting_files = []
2919 potentially_conflicting_files_t = []
2920 potentially_conflicting_files_t += generated_headers
2921 potentially_conflicting_files_t += generated_backend_headers
2922 potentially_conflicting_files_t += generated_backend_sources
2923 potentially_conflicting_files_t += generated_sources
2925 potentially_conflicting_files = []
2927 # convert all sources of potentially conflicting files into uniform shape
2928 foreach t : potentially_conflicting_files_t
2929   potentially_conflicting_files += t.full_path()
2930 endforeach
2931 foreach t1 : configure_files
2932   if meson.version().version_compare('>=0.59')
2933     t = fs.parent(t1) / fs.name(t1)
2934   else
2935     t = '@0@'.format(t1)
2936   endif
2937   potentially_conflicting_files += meson.current_build_dir() / t
2938 endforeach
2939 foreach sub, fnames : generated_sources_ac
2940   sub = meson.build_root() / sub
2941   foreach fname : fnames
2942     potentially_conflicting_files += sub / fname
2943   endforeach
2944 endforeach
2946 # find and report conflicting files
2947 foreach build_path : potentially_conflicting_files
2948   build_path = host_system == 'windows' ? fs.as_posix(build_path) : build_path
2949   # str.replace is in 0.56
2950   src_path = meson.current_source_dir() / build_path.split(meson.current_build_dir() / '')[1]
2951   if fs.exists(src_path) or fs.is_symlink(src_path)
2952     conflicting_files += src_path
2953   endif
2954 endforeach
2955 # XXX: Perhaps we should generate a file that would clean these up? The list
2956 # can be long.
2957 if conflicting_files.length() > 0
2958   errmsg_cleanup = '''
2959 Conflicting files in source directory:
2960   @0@
2962 The conflicting files need to be removed, either by removing the files listed
2963 above, or by running configure and then make maintainer-clean.
2965   errmsg_cleanup = errmsg_cleanup.format(' '.join(conflicting_files))
2966   error(errmsg_nonclean_base.format(errmsg_cleanup))
2967 endif
2971 ###############################################################
2972 # Install targets
2973 ###############################################################
2976 # We want to define additional install targets beyond what meson provides. For
2977 # that we need to define targets depending on nearly everything. We collected
2978 # the results of i18n.gettext() invocations into nls_targets, that also
2979 # includes maintainer targets though. Collect the ones we want as a dependency.
2981 # i18n.gettext() doesn't return the dependencies before 0.60 - but the gettext
2982 # generation happens during install, so that's not a real issue.
2983 nls_mo_targets = []
2984 if libintl.found() and meson.version().version_compare('>=0.60')
2985   # use range() to avoid the flattening of the list that foreach() would do
2986   foreach off : range(0, nls_targets.length())
2987     # i18n.gettext() list containing 1) list of built .mo files 2) maintainer
2988     # -pot target 3) maintainer -pot target
2989     nls_mo_targets += nls_targets[off][0]
2990   endforeach
2991   alias_target('nls', nls_mo_targets)
2992 endif
2995 all_built = [
2996   backend_targets,
2997   bin_targets,
2998   libpq_st,
2999   pl_targets,
3000   contrib_targets,
3001   nls_mo_targets,
3002   testprep_targets,
3003   ecpg_targets,
3006 # Meson's default install target is quite verbose. Provide one that is quiet.
3007 install_quiet = custom_target('install-quiet',
3008   output: 'install-quiet',
3009   build_always_stale: true,
3010   build_by_default: false,
3011   command: [meson_bin, meson_args, 'install', '--quiet', '--no-rebuild'],
3012   depends: all_built,
3015 # Target to install files used for tests, which aren't installed by default
3016 install_test_files_args = [
3017   install_files,
3018   '--prefix', dir_prefix,
3019   '--install', contrib_data_dir, test_install_data,
3020   '--install', dir_lib_pkg, test_install_libs,
3022 run_target('install-test-files',
3023   command: [python] + install_test_files_args,
3024   depends: testprep_targets,
3029 ###############################################################
3030 # Test prep
3031 ###############################################################
3033 # DESTDIR for the installation we'll run tests in
3034 test_install_destdir = meson.build_root() / 'tmp_install/'
3036 # DESTDIR + prefix appropriately munged
3037 if build_system != 'windows'
3038   # On unixoid systems this is trivial, we just prepend the destdir
3039   assert(dir_prefix.startswith('/')) # enforced by meson
3040   test_install_location = '@0@@1@'.format(test_install_destdir, dir_prefix)
3041 else
3042   # drives, drive-relative paths, etc make this complicated on windows, call
3043   # into a copy of meson's logic for it
3044   command = [
3045     python, '-c',
3046     'import sys; from pathlib import PurePath; d1=sys.argv[1]; d2=sys.argv[2]; print(str(PurePath(d1, *PurePath(d2).parts[1:])))',
3047     test_install_destdir, dir_prefix]
3048   test_install_location = run_command(command, check: true).stdout().strip()
3049 endif
3051 meson_install_args = meson_args + ['install'] + {
3052     'meson': ['--quiet', '--only-changed', '--no-rebuild'],
3053     'muon': []
3054 }[meson_impl]
3056 # setup tests should be run first,
3057 # so define priority for these
3058 setup_tests_priority = 100
3059 test('tmp_install',
3060     meson_bin, args: meson_install_args ,
3061     env: {'DESTDIR':test_install_destdir},
3062     priority: setup_tests_priority,
3063     timeout: 300,
3064     is_parallel: false,
3065     suite: ['setup'])
3067 test('install_test_files',
3068     python,
3069     args: install_test_files_args + ['--destdir', test_install_destdir],
3070     priority: setup_tests_priority,
3071     is_parallel: false,
3072     suite: ['setup'])
3074 test_result_dir = meson.build_root() / 'testrun'
3077 # XXX: pg_regress doesn't assign unique ports on windows. To avoid the
3078 # inevitable conflicts from running tests in parallel, hackishly assign
3079 # different ports for different tests.
3081 testport = 40000
3083 test_env = environment()
3085 temp_install_bindir = test_install_location / get_option('bindir')
3086 test_initdb_template = meson.build_root() / 'tmp_install' / 'initdb-template'
3087 test_env.set('PG_REGRESS', pg_regress.full_path())
3088 test_env.set('REGRESS_SHLIB', regress_module.full_path())
3089 test_env.set('INITDB_TEMPLATE', test_initdb_template)
3091 # Test suites that are not safe by default but can be run if selected
3092 # by the user via the whitespace-separated list in variable PG_TEST_EXTRA.
3093 # Export PG_TEST_EXTRA so it can be checked in individual tap tests.
3094 test_env.set('PG_TEST_EXTRA', get_option('PG_TEST_EXTRA'))
3096 # Add the temporary installation to the library search path on platforms where
3097 # that works (everything but windows, basically). On windows everything
3098 # library-like gets installed into bindir, solving that issue.
3099 if library_path_var != ''
3100   test_env.prepend(library_path_var, test_install_location / get_option('libdir'))
3101 endif
3104 # Create (and remove old) initdb template directory. Tests use that, where
3105 # possible, to make it cheaper to run tests.
3107 # Use python to remove the old cached initdb, as we cannot rely on a working
3108 # 'rm' binary on windows.
3109 test('initdb_cache',
3110      python,
3111      args: [
3112        '-c', '''
3113 import shutil
3114 import sys
3115 import subprocess
3117 shutil.rmtree(sys.argv[1], ignore_errors=True)
3118 sp = subprocess.run(sys.argv[2:] + [sys.argv[1]])
3119 sys.exit(sp.returncode)
3120 ''',
3121        test_initdb_template,
3122        temp_install_bindir / 'initdb',
3123        '--auth', 'trust', '--no-sync', '--no-instructions', '--lc-messages=C',
3124        '--no-clean'
3125      ],
3126      priority: setup_tests_priority - 1,
3127      timeout: 300,
3128      is_parallel: false,
3129      env: test_env,
3130      suite: ['setup'])
3134 ###############################################################
3135 # Test Generation
3136 ###############################################################
3138 # When using a meson version understanding exclude_suites, define a
3139 # 'tmp_install' test setup (the default) that excludes tests running against a
3140 # pre-existing install and a 'running' setup that conflicts with creation of
3141 # the temporary installation and tap tests (which don't support running
3142 # against a running server).
3144 running_suites = []
3145 install_suites = []
3146 if meson.version().version_compare('>=0.57')
3147   runningcheck = true
3148 else
3149   runningcheck = false
3150 endif
3152 testwrap = files('src/tools/testwrap')
3154 foreach test_dir : tests
3155   testwrap_base = [
3156     testwrap,
3157     '--basedir', meson.build_root(),
3158     '--srcdir', test_dir['sd'],
3159   ]
3161   foreach kind, v : test_dir
3162     if kind in ['sd', 'bd', 'name']
3163       continue
3164     endif
3166     t = test_dir[kind]
3168     if kind in ['regress', 'isolation', 'ecpg']
3169       if kind == 'regress'
3170         runner = pg_regress
3171         fallback_dbname = 'regression_@0@'
3172       elif kind == 'isolation'
3173         runner = pg_isolation_regress
3174         fallback_dbname = 'isolation_regression_@0@'
3175       elif kind == 'ecpg'
3176         runner = pg_regress_ecpg
3177         fallback_dbname = 'ecpg_regression_@0@'
3178       endif
3180       test_group = test_dir['name']
3181       test_group_running = test_dir['name'] + '-running'
3183       test_output = test_result_dir / test_group / kind
3184       test_output_running = test_result_dir / test_group_running/ kind
3186       # Unless specified by the test, choose a non-conflicting database name,
3187       # to avoid conflicts when running against existing server.
3188       dbname = t.get('dbname',
3189         fallback_dbname.format(test_dir['name']))
3191       test_command_base = [
3192         runner.full_path(),
3193         '--inputdir', t.get('inputdir', test_dir['sd']),
3194         '--expecteddir', t.get('expecteddir', test_dir['sd']),
3195         '--bindir', '',
3196         '--dlpath', test_dir['bd'],
3197         '--max-concurrent-tests=20',
3198         '--dbname', dbname,
3199       ] + t.get('regress_args', [])
3201       test_selection = []
3202       if t.has_key('schedule')
3203         test_selection += ['--schedule', t['schedule'],]
3204       endif
3206       if kind == 'isolation'
3207         test_selection += t.get('specs', [])
3208       else
3209         test_selection += t.get('sql', [])
3210       endif
3212       env = test_env
3213       env.prepend('PATH', temp_install_bindir, test_dir['bd'])
3215       test_kwargs = {
3216         'protocol': 'tap',
3217         'priority': 10,
3218         'timeout': 1000,
3219         'depends': test_deps + t.get('deps', []),
3220         'env': env,
3221       } + t.get('test_kwargs', {})
3223       test(test_group / kind,
3224         python,
3225         args: [
3226           testwrap_base,
3227           '--testgroup', test_group,
3228           '--testname', kind,
3229           '--',
3230           test_command_base,
3231           '--outputdir', test_output,
3232           '--temp-instance', test_output / 'tmp_check',
3233           '--port', testport.to_string(),
3234           test_selection,
3235         ],
3236         suite: test_group,
3237         kwargs: test_kwargs,
3238       )
3239       install_suites += test_group
3241       # some tests can't support running against running DB
3242       if runningcheck and t.get('runningcheck', true)
3243         test(test_group_running / kind,
3244           python,
3245           args: [
3246             testwrap_base,
3247             '--testgroup', test_group_running,
3248             '--testname', kind,
3249             '--',
3250             test_command_base,
3251             '--outputdir', test_output_running,
3252             test_selection,
3253           ],
3254           is_parallel: t.get('runningcheck-parallel', true),
3255           suite: test_group_running,
3256           kwargs: test_kwargs,
3257         )
3258         running_suites += test_group_running
3259       endif
3261       testport += 1
3262     elif kind == 'tap'
3263       testwrap_tap = testwrap_base
3264       if not tap_tests_enabled
3265         testwrap_tap += ['--skip', 'TAP tests not enabled']
3266       endif
3268       test_command = [
3269         perl.path(),
3270         '-I', meson.source_root() / 'src/test/perl',
3271         '-I', test_dir['sd'],
3272       ]
3274       # Add temporary install, the build directory for non-installed binaries and
3275       # also test/ for non-installed test binaries built separately.
3276       env = test_env
3277       env.prepend('PATH', temp_install_bindir, test_dir['bd'], test_dir['bd'] / 'test')
3279       foreach name, value : t.get('env', {})
3280         env.set(name, value)
3281       endforeach
3283       test_group = test_dir['name']
3284       test_kwargs = {
3285         'protocol': 'tap',
3286         'suite': test_group,
3287         'timeout': 1000,
3288         'depends': test_deps + t.get('deps', []),
3289         'env': env,
3290       } + t.get('test_kwargs', {})
3292       foreach onetap : t['tests']
3293         # Make tap test names prettier, remove t/ and .pl
3294         onetap_p = onetap
3295         if onetap_p.startswith('t/')
3296           onetap_p = onetap.split('t/')[1]
3297         endif
3298         if onetap_p.endswith('.pl')
3299           onetap_p = fs.stem(onetap_p)
3300         endif
3302         test(test_dir['name'] / onetap_p,
3303           python,
3304           kwargs: test_kwargs,
3305           args: testwrap_tap + [
3306             '--testgroup', test_dir['name'],
3307             '--testname', onetap_p,
3308             '--', test_command,
3309             test_dir['sd'] / onetap,
3310           ],
3311         )
3312       endforeach
3313       install_suites += test_group
3314     else
3315       error('unknown kind @0@ of test in @1@'.format(kind, test_dir['sd']))
3316     endif
3318   endforeach # kinds of tests
3320 endforeach # directories with tests
3322 # repeat condition so meson realizes version dependency
3323 if meson.version().version_compare('>=0.57')
3324   add_test_setup('tmp_install',
3325     is_default: true,
3326     exclude_suites: running_suites)
3327   add_test_setup('running',
3328     exclude_suites: ['setup'] + install_suites)
3329 endif
3333 ###############################################################
3334 # Pseudo targets
3335 ###############################################################
3337 alias_target('backend', backend_targets)
3338 alias_target('bin', bin_targets + [libpq_st])
3339 alias_target('pl', pl_targets)
3340 alias_target('contrib', contrib_targets)
3341 alias_target('testprep', testprep_targets)
3343 alias_target('world', all_built, docs)
3344 alias_target('install-world', install_quiet, installdocs)
3346 run_target('help',
3347   command: [
3348     perl, '-ne', 'next if /^#/; print',
3349     files('doc/src/sgml/targets-meson.txt'),
3350   ]
3355 ###############################################################
3356 # The End, The End, My Friend
3357 ###############################################################
3359 if meson.version().version_compare('>=0.57')
3361   summary(
3362     {
3363       'data block size': '@0@ kB'.format(cdata.get('BLCKSZ') / 1024),
3364       'WAL block size': '@0@ kB'.format(cdata.get('XLOG_BLCKSZ') / 1024),
3365       'segment size': get_option('segsize_blocks') != 0 ?
3366         '@0@ blocks'.format(cdata.get('RELSEG_SIZE')) :
3367         '@0@ GB'.format(get_option('segsize')),
3368     },
3369     section: 'Data layout',
3370   )
3372   summary(
3373     {
3374       'host system': '@0@ @1@'.format(host_system, host_cpu),
3375       'build system': '@0@ @1@'.format(build_machine.system(),
3376                                        build_machine.cpu_family()),
3377     },
3378     section: 'System',
3379   )
3381   summary(
3382     {
3383       'linker': '@0@'.format(cc.get_linker_id()),
3384       'C compiler': '@0@ @1@'.format(cc.get_id(), cc.version()),
3385     },
3386     section: 'Compiler',
3387   )
3389   summary(
3390     {
3391       'CPP FLAGS': ' '.join(cppflags),
3392       'C FLAGS, functional': ' '.join(cflags),
3393       'C FLAGS, warnings': ' '.join(cflags_warn),
3394       'C FLAGS, modules': ' '.join(cflags_mod),
3395       'C FLAGS, user specified': ' '.join(get_option('c_args')),
3396       'LD FLAGS': ' '.join(ldflags + get_option('c_link_args')),
3397     },
3398     section: 'Compiler Flags',
3399   )
3401   if llvm.found()
3402     summary(
3403       {
3404         'C++ compiler': '@0@ @1@'.format(cpp.get_id(), cpp.version()),
3405       },
3406       section: 'Compiler',
3407     )
3409     summary(
3410       {
3411         'C++ FLAGS, functional': ' '.join(cxxflags),
3412         'C++ FLAGS, warnings': ' '.join(cxxflags_warn),
3413         'C++ FLAGS, user specified': ' '.join(get_option('cpp_args')),
3414       },
3415       section: 'Compiler Flags',
3416     )
3417   endif
3419   summary(
3420     {
3421       'bison': '@0@ @1@'.format(bison.full_path(), bison_version),
3422       'dtrace': dtrace,
3423       'flex': '@0@ @1@'.format(flex.full_path(), flex_version),
3424     },
3425     section: 'Programs',
3426   )
3428   summary(
3429     {
3430       'bonjour': bonjour,
3431       'bsd_auth': bsd_auth,
3432       'docs': docs_dep,
3433       'docs_pdf': docs_pdf_dep,
3434       'gss': gssapi,
3435       'icu': icu,
3436       'ldap': ldap,
3437       'libxml': libxml,
3438       'libxslt': libxslt,
3439       'llvm': llvm,
3440       'lz4': lz4,
3441       'nls': libintl,
3442       'openssl': ssl,
3443       'pam': pam,
3444       'plperl': perl_dep,
3445       'plpython': python3_dep,
3446       'pltcl': tcl_dep,
3447       'readline': readline,
3448       'selinux': selinux,
3449       'systemd': systemd,
3450       'uuid': uuid,
3451       'zlib': zlib,
3452       'zstd': zstd,
3453     },
3454     section: 'External libraries',
3455   )
3457 endif