pg_upgrade: include additional detail in cluster check
[pgsql.git] / meson.build
blob04ea34885226a725984b2a7855dd9abeca0610fd
1 # Copyright (c) 2022-2023, PostgreSQL Global Development Group
3 # Entry point for building PostgreSQL with meson
5 # Good starting points for writing meson.build files are:
6 #  - https://mesonbuild.com/Syntax.html
7 #  - https://mesonbuild.com/Reference-manual.html
9 project('postgresql',
10   ['c'],
11   version: '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 maintainer-clean in the source tree.'
76   error(errmsg_nonclean_base.format(errmsg_cleanup))
77 endif
81 ###############################################################
82 # Variables to be determined
83 ###############################################################
85 postgres_inc_d = ['src/include']
86 postgres_inc_d += get_option('extra_include_dirs')
88 postgres_lib_d = get_option('extra_lib_dirs')
90 cppflags = []
92 cflags = []
93 cxxflags = []
94 cflags_warn = []
95 cxxflags_warn = []
96 cflags_mod = []
97 cxxflags_mod = []
99 ldflags = []
100 ldflags_be = []
101 ldflags_sl = []
102 ldflags_mod = []
104 test_c_args = []
106 os_deps = []
107 backend_both_deps = []
108 backend_deps = []
109 libpq_deps = []
111 pg_sysroot = ''
113 # source of data for pg_config.h etc
114 cdata = configuration_data()
118 ###############################################################
119 # Version and other metadata
120 ###############################################################
122 pg_version = meson.project_version()
124 if pg_version.endswith('devel')
125   pg_version_arr = [pg_version.split('devel')[0], '0']
126 elif pg_version.contains('beta')
127   pg_version_arr = [pg_version.split('beta')[0], '0']
128 elif pg_version.contains('rc')
129   pg_version_arr = [pg_version.split('rc')[0], '0']
130 else
131   pg_version_arr = pg_version.split('.')
132 endif
134 pg_version_major = pg_version_arr[0].to_int()
135 pg_version_minor = pg_version_arr[1].to_int()
136 pg_version_num = (pg_version_major * 10000) + pg_version_minor
138 pg_url = 'https://www.postgresql.org/'
140 cdata.set_quoted('PACKAGE_NAME', 'PostgreSQL')
141 cdata.set_quoted('PACKAGE_BUGREPORT', 'pgsql-bugs@lists.postgresql.org')
142 cdata.set_quoted('PACKAGE_URL', pg_url)
143 cdata.set_quoted('PACKAGE_VERSION', pg_version)
144 cdata.set_quoted('PACKAGE_STRING', 'PostgreSQL @0@'.format(pg_version))
145 cdata.set_quoted('PACKAGE_TARNAME', 'postgresql')
147 pg_version += get_option('extra_version')
148 cdata.set_quoted('PG_VERSION', pg_version)
149 cdata.set_quoted('PG_MAJORVERSION', pg_version_major.to_string())
150 cdata.set('PG_MAJORVERSION_NUM', pg_version_major)
151 cdata.set('PG_MINORVERSION_NUM', pg_version_minor)
152 cdata.set('PG_VERSION_NUM', pg_version_num)
153 # PG_VERSION_STR is built later, it depends on compiler test results
154 cdata.set_quoted('CONFIGURE_ARGS', '')
158 ###############################################################
159 # Basic platform specific configuration
160 ###############################################################
162 # meson's system names don't quite map to our "traditional" names. In some
163 # places we need the "traditional" name, e.g., for mapping
164 # src/include/port/$os.h to src/include/pg_config_os.h. Define portname for
165 # that purpose.
166 portname = host_system
168 exesuffix = '' # overridden below where necessary
169 dlsuffix = '.so' # overridden below where necessary
170 library_path_var = 'LD_LIBRARY_PATH'
172 # Format of file to control exports from libraries, and how to pass them to
173 # the compiler. For export_fmt @0@ is the path to the file export file.
174 export_file_format = 'gnu'
175 export_file_suffix = 'list'
176 export_fmt = '-Wl,--version-script=@0@'
178 # Flags to add when linking a postgres extension, @0@ is path to
179 # the relevant object on the platform.
180 mod_link_args_fmt = []
182 memset_loop_limit = 1024
184 # Choice of shared memory and semaphore implementation
185 shmem_kind = 'sysv'
186 sema_kind = 'sysv'
188 # We implement support for some operating systems by pretending they're
189 # another. Map here, before determining system properties below
190 if host_system == 'dragonfly'
191   # apparently the most similar
192   host_system = 'netbsd'
193 endif
195 if host_system == 'aix'
196   library_path_var = 'LIBPATH'
198   export_file_format = 'aix'
199   export_fmt = '-Wl,-bE:@0@'
200   mod_link_args_fmt = ['-Wl,-bI:@0@']
201   mod_link_with_dir = 'libdir'
202   mod_link_with_name = '@0@.imp'
204   # M:SRE sets a flag indicating that an object is a shared library. Seems to
205   # work in some circumstances without, but required in others.
206   ldflags_sl += '-Wl,-bM:SRE'
207   ldflags_be += '-Wl,-brtllib'
209   # Native memset() is faster, tested on:
210   # - AIX 5.1 and 5.2, XLC 6.0 (IBM's cc)
211   # - AIX 5.3 ML3, gcc 4.0.1
212   memset_loop_limit = 0
214 elif host_system == 'cygwin'
215   sema_kind = 'unnamed_posix'
216   cppflags += '-D_GNU_SOURCE'
217   dlsuffix = '.dll'
218   mod_link_args_fmt = ['@0@']
219   mod_link_with_name = 'lib@0@.exe.a'
220   mod_link_with_dir = 'libdir'
222 elif host_system == 'darwin'
223   dlsuffix = '.dylib'
224   library_path_var = 'DYLD_LIBRARY_PATH'
226   export_file_format = 'darwin'
227   export_fmt = '-exported_symbols_list=@0@'
229   mod_link_args_fmt = ['-bundle_loader', '@0@']
230   mod_link_with_dir = 'bindir'
231   mod_link_with_name = '@0@'
233   sysroot_args = [files('src/tools/darwin_sysroot'), get_option('darwin_sysroot')]
234   pg_sysroot = run_command(sysroot_args, check:true).stdout().strip()
235   message('darwin sysroot: @0@'.format(pg_sysroot))
236   if pg_sysroot != ''
237     cflags += ['-isysroot', pg_sysroot]
238     ldflags += ['-isysroot', pg_sysroot]
239   endif
240   # meson defaults to -Wl,-undefined,dynamic_lookup for modules, which we
241   # don't want because a) it's different from what we do for autoconf, b) it
242   # causes warnings starting in macOS Ventura
243   ldflags_mod += ['-Wl,-undefined,error']
245 elif host_system == 'freebsd'
246   sema_kind = 'unnamed_posix'
248 elif host_system == 'linux'
249   sema_kind = 'unnamed_posix'
250   cppflags += '-D_GNU_SOURCE'
252 elif host_system == 'netbsd'
253   # We must resolve all dynamic linking in the core server at program start.
254   # Otherwise the postmaster can self-deadlock due to signals interrupting
255   # resolution of calls, since NetBSD's linker takes a lock while doing that
256   # and some postmaster signal handlers do things that will also acquire that
257   # lock.  As long as we need "-z now", might as well specify "-z relro" too.
258   # While there's not a hard reason to adopt these settings for our other
259   # executables, there's also little reason not to, so just add them to
260   # LDFLAGS.
261   ldflags += ['-Wl,-z,now', '-Wl,-z,relro']
263 elif host_system == 'openbsd'
264   # you're ok
266 elif host_system == 'sunos'
267   portname = 'solaris'
268   export_fmt = '-Wl,-M@0@'
269   cppflags += '-D_POSIX_PTHREAD_SEMANTICS'
271 elif host_system == 'windows'
272   portname = 'win32'
273   exesuffix = '.exe'
274   dlsuffix = '.dll'
275   library_path_var = ''
277   export_file_format = 'win'
278   export_file_suffix = 'def'
279   if cc.get_id() == 'msvc'
280     export_fmt = '/DEF:@0@'
281     mod_link_with_name = '@0@.exe.lib'
282   else
283     export_fmt = '@0@'
284     mod_link_with_name = 'lib@0@.exe.a'
285   endif
286   mod_link_args_fmt = ['@0@']
287   mod_link_with_dir = 'libdir'
289   shmem_kind = 'win32'
290   sema_kind = 'win32'
292   cdata.set('WIN32_STACK_RLIMIT', 4194304)
293   if cc.get_id() == 'msvc'
294     ldflags += '/INCREMENTAL:NO'
295     ldflags += '/STACK:@0@'.format(cdata.get('WIN32_STACK_RLIMIT'))
296     # ldflags += '/nxcompat' # generated by msbuild, should have it for ninja?
297   else
298     ldflags += '-Wl,--stack,@0@'.format(cdata.get('WIN32_STACK_RLIMIT'))
299     # Need to allow multiple definitions, we e.g. want to override getopt.
300     ldflags += '-Wl,--allow-multiple-definition'
301     # Ensure we get MSVC-like linking behavior.
302     ldflags += '-Wl,--disable-auto-import'
303   endif
305   os_deps += cc.find_library('ws2_32', required: true)
306   secur32_dep = cc.find_library('secur32', required: true)
307   backend_deps += secur32_dep
308   libpq_deps += secur32_dep
310   postgres_inc_d += 'src/include/port/win32'
311   if cc.get_id() == 'msvc'
312     postgres_inc_d += 'src/include/port/win32_msvc'
313   endif
315   windows = import('windows')
317 else
318   # XXX: Should we add an option to override the host_system as an escape
319   # hatch?
320   error('unknown host system: @0@'.format(host_system))
321 endif
325 ###############################################################
326 # Program paths
327 ###############################################################
329 # External programs
330 perl = find_program(get_option('PERL'), required: true, native: true)
331 python = find_program(get_option('PYTHON'), required: true, native: true)
332 flex = find_program(get_option('FLEX'), native: true, version: '>= 2.5.35')
333 bison = find_program(get_option('BISON'), native: true, version: '>= 2.3')
334 sed = find_program(get_option('SED'), 'sed', native: true)
335 prove = find_program(get_option('PROVE'), native: true, required: false)
336 tar = find_program(get_option('TAR'), native: true)
337 gzip = find_program(get_option('GZIP'), native: true)
338 program_lz4 = find_program(get_option('LZ4'), native: true, required: false)
339 openssl = find_program(get_option('OPENSSL'), native: true, required: false)
340 program_zstd = find_program(get_option('ZSTD'), native: true, required: false)
341 dtrace = find_program(get_option('DTRACE'), native: true, required: get_option('dtrace'))
342 missing = find_program('config/missing', native: true)
343 cp = find_program('cp', required: false, native: true)
344 xmllint_bin = find_program(get_option('XMLLINT'), native: true, required: false)
345 xsltproc_bin = find_program(get_option('XSLTPROC'), native: true, required: false)
347 bison_flags = []
348 if bison.found()
349   bison_version_c = run_command(bison, '--version', check: true)
350   # bison version string helpfully is something like
351   # >>bison (GNU bison) 3.8.1<<
352   bison_version = bison_version_c.stdout().split(' ')[3].split('\n')[0]
353   if bison_version.version_compare('>=3.0')
354     bison_flags += ['-Wno-deprecated']
355   endif
356 endif
357 bison_cmd = [bison, bison_flags, '-o', '@OUTPUT0@', '-d', '@INPUT@']
358 bison_kw = {
359   'output': ['@BASENAME@.c', '@BASENAME@.h'],
360   'command': bison_cmd,
363 flex_flags = []
364 if flex.found()
365   flex_version_c = run_command(flex, '--version', check: true)
366   flex_version = flex_version_c.stdout().split(' ')[1].split('\n')[0]
367 endif
368 flex_wrapper = files('src/tools/pgflex')
369 flex_cmd = [python, flex_wrapper,
370   '--builddir', '@BUILD_ROOT@',
371   '--srcdir', '@SOURCE_ROOT@',
372   '--privatedir', '@PRIVATE_DIR@',
373   '--flex', flex, '--perl', perl,
374   '-i', '@INPUT@', '-o', '@OUTPUT0@',
377 wget = find_program('wget', required: false, native: true)
378 wget_flags = ['-O', '@OUTPUT0@', '--no-use-server-timestamps']
380 install_files = files('src/tools/install_files')
384 ###############################################################
385 # Path to meson (for tests etc)
386 ###############################################################
388 # NB: this should really be part of meson, see
389 # https://github.com/mesonbuild/meson/issues/8511
390 meson_binpath_r = run_command(python, 'src/tools/find_meson', check: true)
392 if meson_binpath_r.stdout() == ''
393   error('huh, could not run find_meson.\nerrcode: @0@\nstdout: @1@\nstderr: @2@'.format(
394     meson_binpath_r.returncode(),
395     meson_binpath_r.stdout(),
396     meson_binpath_r.stderr()))
397 endif
399 meson_binpath_s = meson_binpath_r.stdout().split('\n')
400 meson_binpath_len = meson_binpath_s.length()
402 if meson_binpath_len < 1
403   error('unexpected introspect line @0@'.format(meson_binpath_r.stdout()))
404 endif
406 i = 0
407 meson_impl = ''
408 meson_binpath = ''
409 meson_args = []
410 foreach e : meson_binpath_s
411   if i == 0
412     meson_impl = e
413   elif i == 1
414     meson_binpath = e
415   else
416     meson_args += e
417   endif
418   i += 1
419 endforeach
421 if meson_impl not in ['muon', 'meson']
422   error('unknown meson implementation "@0@"'.format(meson_impl))
423 endif
425 meson_bin = find_program(meson_binpath, native: true)
429 ###############################################################
430 # Option Handling
431 ###############################################################
433 cdata.set('USE_ASSERT_CHECKING', get_option('cassert') ? 1 : false)
435 blocksize = get_option('blocksize').to_int() * 1024
437 if get_option('segsize_blocks') != 0
438   if get_option('segsize') != 1
439     warning('both segsize and segsize_blocks specified, segsize_blocks wins')
440   endif
442   segsize = get_option('segsize_blocks')
443 else
444   segsize = (get_option('segsize') * 1024 * 1024 * 1024) / blocksize
445 endif
447 cdata.set('BLCKSZ', blocksize, description:
448 '''Size of a disk block --- this also limits the size of a tuple. You can set
449    it bigger if you need bigger tuples (although TOAST should reduce the need
450    to have large tuples, since fields can be spread across multiple tuples).
451    BLCKSZ must be a power of 2. The maximum possible value of BLCKSZ is
452    currently 2^15 (32768). This is determined by the 15-bit widths of the
453    lp_off and lp_len fields in ItemIdData (see include/storage/itemid.h).
454    Changing BLCKSZ requires an initdb.''')
456 cdata.set('XLOG_BLCKSZ', get_option('wal_blocksize').to_int() * 1024)
457 cdata.set('RELSEG_SIZE', segsize)
458 cdata.set('DEF_PGPORT', get_option('pgport'))
459 cdata.set_quoted('DEF_PGPORT_STR', get_option('pgport').to_string())
460 cdata.set_quoted('PG_KRB_SRVNAM', get_option('krb_srvnam'))
461 if get_option('system_tzdata') != ''
462   cdata.set_quoted('SYSTEMTZDIR', get_option('system_tzdata'))
463 endif
467 ###############################################################
468 # Directories
469 ###############################################################
471 # These are set by the equivalent --xxxdir configure options.  We
472 # append "postgresql" to some of them, if the string does not already
473 # contain "pgsql" or "postgres", in order to avoid directory clutter.
475 pkg = 'postgresql'
477 dir_prefix = get_option('prefix')
479 dir_prefix_contains_pg = (dir_prefix.contains('pgsql') or dir_prefix.contains('postgres'))
481 dir_bin = get_option('bindir')
483 dir_data = get_option('datadir')
484 if not (dir_prefix_contains_pg or dir_data.contains('pgsql') or dir_data.contains('postgres'))
485   dir_data = dir_data / pkg
486 endif
488 dir_sysconf = get_option('sysconfdir')
489 if not (dir_prefix_contains_pg or dir_sysconf.contains('pgsql') or dir_sysconf.contains('postgres'))
490   dir_sysconf = dir_sysconf / pkg
491 endif
493 dir_lib = get_option('libdir')
495 dir_lib_pkg = dir_lib
496 if not (dir_prefix_contains_pg or dir_lib_pkg.contains('pgsql') or dir_lib_pkg.contains('postgres'))
497   dir_lib_pkg = dir_lib_pkg / pkg
498 endif
500 dir_pgxs = dir_lib_pkg / 'pgxs'
502 dir_include = get_option('includedir')
504 dir_include_pkg = dir_include
505 dir_include_pkg_rel = ''
506 if not (dir_prefix_contains_pg or dir_include_pkg.contains('pgsql') or dir_include_pkg.contains('postgres'))
507   dir_include_pkg = dir_include_pkg / pkg
508   dir_include_pkg_rel = pkg
509 endif
511 dir_man = get_option('mandir')
513 # FIXME: These used to be separately configurable - worth adding?
514 dir_doc = get_option('datadir') / 'doc' / 'postgresql'
515 dir_doc_html = dir_doc / 'html'
517 dir_locale = get_option('localedir')
520 # Derived values
521 dir_bitcode = dir_lib_pkg / 'bitcode'
522 dir_include_internal = dir_include_pkg / 'internal'
523 dir_include_server = dir_include_pkg / 'server'
524 dir_include_extension = dir_include_server / 'extension'
525 dir_data_extension = dir_data / 'extension'
529 ###############################################################
530 # Search paths, preparation for compiler tests
532 # NB: Arguments added later are not automatically used for subsequent
533 # configuration-time checks (so they are more isolated). If they should be
534 # used, they need to be added to test_c_args as well.
535 ###############################################################
537 postgres_inc = [include_directories(postgres_inc_d)]
538 test_lib_d = postgres_lib_d
539 test_c_args = cppflags + cflags
543 ###############################################################
544 # Library: bsd-auth
545 ###############################################################
547 bsd_authopt = get_option('bsd_auth')
548 bsd_auth = not_found_dep
549 if cc.check_header('bsd_auth.h', required: bsd_authopt,
550     args: test_c_args, include_directories: postgres_inc)
551   cdata.set('USE_BSD_AUTH', 1)
552   bsd_auth = declare_dependency()
553 endif
557 ###############################################################
558 # Library: bonjour
560 # For now don't search for DNSServiceRegister in a library - only Apple's
561 # Bonjour implementation, which is always linked, works.
562 ###############################################################
564 bonjouropt = get_option('bonjour')
565 bonjour = not_found_dep
566 if cc.check_header('dns_sd.h', required: bonjouropt,
567     args: test_c_args, include_directories: postgres_inc) and \
568    cc.has_function('DNSServiceRegister',
569     args: test_c_args, include_directories: postgres_inc)
570   cdata.set('USE_BONJOUR', 1)
571   bonjour = declare_dependency()
572 endif
576 ###############################################################
577 # Option: docs in HTML and man page format
578 ###############################################################
580 docs_opt = get_option('docs')
581 docs_dep = not_found_dep
582 if not docs_opt.disabled()
583   if xmllint_bin.found() and xsltproc_bin.found()
584     docs_dep = declare_dependency()
585   elif docs_opt.enabled()
586     error('missing required tools for docs in HTML / man page format')
587   endif
588 endif
592 ###############################################################
593 # Option: docs in PDF format
594 ###############################################################
596 docs_pdf_opt = get_option('docs_pdf')
597 docs_pdf_dep = not_found_dep
598 if not docs_pdf_opt.disabled()
599   fop = find_program(get_option('FOP'), native: true, required: docs_pdf_opt)
600   if xmllint_bin.found() and xsltproc_bin.found() and fop.found()
601     docs_pdf_dep = declare_dependency()
602   elif docs_pdf_opt.enabled()
603     error('missing required tools for docs in PDF format')
604   endif
605 endif
609 ###############################################################
610 # Library: GSSAPI
611 ###############################################################
613 gssapiopt = get_option('gssapi')
614 krb_srvtab = ''
615 have_gssapi = false
616 if not gssapiopt.disabled()
617   gssapi = dependency('krb5-gssapi', required: gssapiopt)
618   have_gssapi = gssapi.found()
620   if not have_gssapi
621   elif cc.check_header('gssapi/gssapi.h', dependencies: gssapi, required: false,
622       args: test_c_args, include_directories: postgres_inc)
623     cdata.set('HAVE_GSSAPI_GSSAPI_H', 1)
624   elif cc.check_header('gssapi.h', args: test_c_args, dependencies: gssapi, required: gssapiopt)
625     cdata.set('HAVE_GSSAPI_H', 1)
626   else
627     have_gssapi = false
628   endif
630   if not have_gssapi
631   elif cc.check_header('gssapi/gssapi_ext.h', dependencies: gssapi, required: false,
632       args: test_c_args, include_directories: postgres_inc)
633     cdata.set('HAVE_GSSAPI_GSSAPI_EXT_H', 1)
634   elif cc.check_header('gssapi_ext.h', args: test_c_args, dependencies: gssapi, required: gssapiopt)
635     cdata.set('HAVE_GSSAPI_EXT_H', 1)
636   else
637     have_gssapi = false
638   endif
640   if not have_gssapi
641   elif cc.has_function('gss_store_cred_into', dependencies: gssapi,
642       args: test_c_args, include_directories: postgres_inc)
643     cdata.set('ENABLE_GSS', 1)
645     krb_srvtab = 'FILE:/@0@/krb5.keytab)'.format(get_option('sysconfdir'))
646     cdata.set_quoted('PG_KRB_SRVTAB', krb_srvtab)
647   elif gssapiopt.enabled()
648     error('''could not find function 'gss_store_cred_into' required for GSSAPI''')
649   else
650     have_gssapi = false
651   endif
652 endif
653 if not have_gssapi
654   gssapi = not_found_dep
655 endif
659 ###############################################################
660 # Library: ldap
661 ###############################################################
663 ldapopt = get_option('ldap')
664 if ldapopt.disabled()
665   ldap = not_found_dep
666   ldap_r = not_found_dep
667 elif host_system == 'windows'
668   ldap = cc.find_library('wldap32', required: ldapopt)
669   ldap_r = ldap
670 else
671   # macos framework dependency is buggy for ldap (one can argue whether it's
672   # Apple's or meson's fault), leading to an endless recursion with ldap.h
673   # including itself. See https://github.com/mesonbuild/meson/issues/10002
674   # Luckily we only need pkg-config support, so the workaround isn't
675   # complicated.
676   ldap = dependency('ldap', method: 'pkg-config', required: false)
677   ldap_r = ldap
679   # Before 2.5 openldap didn't have a pkg-config file, and it might not be
680   # installed
681   if not ldap.found()
682     ldap = cc.find_library('ldap', required: ldapopt, dirs: test_lib_d,
683       has_headers: 'ldap.h', header_include_directories: postgres_inc)
685     # The separate ldap_r library only exists in OpenLDAP < 2.5, and if we
686     # have 2.5 or later, we shouldn't even probe for ldap_r (we might find a
687     # library from a separate OpenLDAP installation).  The most reliable
688     # way to check that is to check for a function introduced in 2.5.
689     if not ldap.found()
690       # don't have ldap, we shouldn't check for ldap_r
691     elif cc.has_function('ldap_verify_credentials',
692         dependencies: ldap, args: test_c_args)
693       ldap_r = ldap # ldap >= 2.5, no need for ldap_r
694     else
696       # Use ldap_r for FE if available, else assume ldap is thread-safe.
697       ldap_r = cc.find_library('ldap_r', required: false, dirs: test_lib_d,
698         has_headers: 'ldap.h', header_include_directories: postgres_inc)
699       if not ldap_r.found()
700         ldap_r = ldap
701       else
702         # On some platforms ldap_r fails to link without PTHREAD_LIBS.
703         ldap_r = declare_dependency(dependencies: [ldap_r, thread_dep])
704       endif
706       # PostgreSQL sometimes loads libldap_r and plain libldap into the same
707       # process.  Check for OpenLDAP versions known not to tolerate doing so;
708       # assume non-OpenLDAP implementations are safe.  The dblink test suite
709       # exercises the hazardous interaction directly.
710       compat_test_code = '''
711 #include <ldap.h>
712 #if !defined(LDAP_VENDOR_VERSION) || \
713      (defined(LDAP_API_FEATURE_X_OPENLDAP) && \
714       LDAP_VENDOR_VERSION >= 20424 && LDAP_VENDOR_VERSION <= 20431)
715 choke me
716 #endif
718       if not cc.compiles(compat_test_code,
719           name: 'LDAP implementation compatible',
720           dependencies: ldap, args: test_c_args)
721         warning('''
722 *** With OpenLDAP versions 2.4.24 through 2.4.31, inclusive, each backend
723 *** process that loads libpq (via WAL receiver, dblink, or postgres_fdw) and
724 *** also uses LDAP will crash on exit.''')
725       endif
726     endif
727   endif
729   if ldap.found() and cc.has_function('ldap_initialize',
730       dependencies: ldap, args: test_c_args)
731     cdata.set('HAVE_LDAP_INITIALIZE', 1)
732   endif
733 endif
735 if ldap.found()
736   assert(ldap_r.found())
737   cdata.set('USE_LDAP', 1)
738 else
739   assert(not ldap_r.found())
740 endif
744 ###############################################################
745 # Library: LLVM
746 ###############################################################
748 llvmopt = get_option('llvm')
749 llvm = not_found_dep
750 if add_languages('cpp', required: llvmopt, native: false)
751   llvm = dependency('llvm', version: '>=3.9', method: 'config-tool', required: llvmopt)
753   if llvm.found()
755     cdata.set('USE_LLVM', 1)
757     cpp = meson.get_compiler('cpp')
759     llvm_binpath = llvm.get_variable(configtool: 'bindir')
761     ccache = find_program('ccache', native: true, required: false)
762     clang = find_program(llvm_binpath / 'clang', required: true)
763   endif
764 elif llvmopt.auto()
765   message('llvm requires a C++ compiler')
766 endif
770 ###############################################################
771 # Library: icu
772 ###############################################################
774 icuopt = get_option('icu')
775 if not icuopt.disabled()
776   icu = dependency('icu-uc', required: icuopt)
777   icu_i18n = dependency('icu-i18n', required: icuopt)
779   if icu.found()
780     cdata.set('USE_ICU', 1)
781   endif
783 else
784   icu = not_found_dep
785   icu_i18n = not_found_dep
786 endif
790 ###############################################################
791 # Library: libxml
792 ###############################################################
794 libxmlopt = get_option('libxml')
795 if not libxmlopt.disabled()
796   libxml = dependency('libxml-2.0', required: libxmlopt, version: '>= 2.6.23')
798   if libxml.found()
799     cdata.set('USE_LIBXML', 1)
800   endif
801 else
802   libxml = not_found_dep
803 endif
807 ###############################################################
808 # Library: libxslt
809 ###############################################################
811 libxsltopt = get_option('libxslt')
812 if not libxsltopt.disabled()
813   libxslt = dependency('libxslt', required: libxsltopt)
815   if libxslt.found()
816     cdata.set('USE_LIBXSLT', 1)
817   endif
818 else
819   libxslt = not_found_dep
820 endif
824 ###############################################################
825 # Library: lz4
826 ###############################################################
828 lz4opt = get_option('lz4')
829 if not lz4opt.disabled()
830   lz4 = dependency('liblz4', required: lz4opt)
832   if lz4.found()
833     cdata.set('USE_LZ4', 1)
834     cdata.set('HAVE_LIBLZ4', 1)
835   endif
837 else
838   lz4 = not_found_dep
839 endif
843 ###############################################################
844 # Library: Tcl (for pltcl)
846 # NB: tclConfig.sh is used in autoconf build for getting
847 # TCL_SHARED_BUILD, TCL_INCLUDE_SPEC, TCL_LIBS and TCL_LIB_SPEC
848 # variables. For now we have not seen a need to copy
849 # that behaviour to the meson build.
850 ###############################################################
852 tclopt = get_option('pltcl')
853 tcl_version = get_option('tcl_version')
854 tcl_dep = not_found_dep
855 if not tclopt.disabled()
857   # via pkg-config
858   tcl_dep = dependency(tcl_version, required: false)
860   if not tcl_dep.found()
861     tcl_dep = cc.find_library(tcl_version,
862       required: tclopt,
863       dirs: test_lib_d)
864   endif
866   if not cc.has_header('tcl.h', dependencies: tcl_dep, required: tclopt)
867     tcl_dep = not_found_dep
868   endif
869 endif
873 ###############################################################
874 # Library: pam
875 ###############################################################
877 pamopt = get_option('pam')
878 if not pamopt.disabled()
879   pam = dependency('pam', required: false)
881   if not pam.found()
882     pam = cc.find_library('pam', required: pamopt, dirs: test_lib_d)
883   endif
885   if pam.found()
886     pam_header_found = false
888     # header file <security/pam_appl.h> or <pam/pam_appl.h> is required for PAM.
889     if cc.check_header('security/pam_appl.h', dependencies: pam, required: false,
890         args: test_c_args, include_directories: postgres_inc)
891       cdata.set('HAVE_SECURITY_PAM_APPL_H', 1)
892       pam_header_found = true
893     elif cc.check_header('pam/pam_appl.h', dependencies: pam, required: pamopt,
894         args: test_c_args, include_directories: postgres_inc)
895       cdata.set('HAVE_PAM_PAM_APPL_H', 1)
896       pam_header_found = true
897     endif
899     if pam_header_found
900       cdata.set('USE_PAM', 1)
901     else
902       pam = not_found_dep
903     endif
904   endif
905 else
906   pam = not_found_dep
907 endif
911 ###############################################################
912 # Library: Perl (for plperl)
913 ###############################################################
915 perlopt = get_option('plperl')
916 perl_dep = not_found_dep
917 if not perlopt.disabled()
918   perl_may_work = true
920   # First verify that perl has the necessary dependencies installed
921   perl_mods = run_command(
922     [perl,
923      '-MConfig', '-MOpcode', '-MExtUtils::Embed', '-MExtUtils::ParseXS',
924      '-e', ''],
925     check: false)
926   if perl_mods.returncode() != 0
927     perl_may_work = false
928     perl_msg = 'perl installation does not have the required modules'
929   endif
931   # Then inquire perl about its configuration
932   if perl_may_work
933     perl_conf_cmd = [perl, '-MConfig', '-e', 'print $Config{$ARGV[0]}']
934     perlversion = run_command(perl_conf_cmd, 'api_versionstring', check: true).stdout()
935     archlibexp = run_command(perl_conf_cmd, 'archlibexp', check: true).stdout()
936     privlibexp = run_command(perl_conf_cmd, 'privlibexp', check: true).stdout()
937     useshrplib = run_command(perl_conf_cmd, 'useshrplib', check: true).stdout()
939     perl_inc_dir = '@0@/CORE'.format(archlibexp)
941     if perlversion.version_compare('< 5.14')
942       perl_may_work = false
943       perl_msg = 'Perl version 5.14 or later is required, but this is @0@'.format(perlversion)
944     elif useshrplib != 'true'
945       perl_may_work = false
946       perl_msg = 'need a shared perl'
947     endif
948   endif
950   if perl_may_work
951     # On most platforms, archlibexp is also where the Perl include files live ...
952     perl_ccflags = ['-I@0@'.format(perl_inc_dir)]
953     # ... but on newer macOS versions, we must use -iwithsysroot to look
954     # under sysroot
955     if not fs.is_file('@0@/perl.h'.format(perl_inc_dir)) and \
956        fs.is_file('@0@@1@/perl.h'.format(pg_sysroot, perl_inc_dir))
957       perl_ccflags = ['-iwithsysroot', perl_inc_dir]
958     endif
960     # check compiler finds header
961     if not cc.has_header('perl.h', required: false,
962         args: test_c_args + perl_ccflags, include_directories: postgres_inc)
963       perl_may_work = false
964       perl_msg = 'missing perl.h'
965     endif
966   endif
968   if perl_may_work
969     perl_ccflags_r = run_command(perl_conf_cmd, 'ccflags', check: true).stdout()
971     # See comments for PGAC_CHECK_PERL_EMBED_CCFLAGS in perl.m4
972     foreach flag : perl_ccflags_r.split(' ')
973       if flag.startswith('-D') and \
974           (not flag.startswith('-D_') or flag == '_USE_32BIT_TIME_T')
975         perl_ccflags += flag
976       endif
977     endforeach
979     if host_system == 'windows'
980       perl_ccflags += ['-DPLPERL_HAVE_UID_GID']
982       if cc.get_id() == 'msvc'
983         # prevent binary mismatch between MSVC built plperl and Strawberry or
984         # msys ucrt perl libraries
985         perl_ccflags += ['-DNO_THREAD_SAFE_LOCALE']
986       endif
987     endif
989     message('CCFLAGS recommended by perl: @0@'.format(perl_ccflags_r))
990     message('CCFLAGS for embedding perl: @0@'.format(' '.join(perl_ccflags)))
992     # We are after Embed's ldopts, but without the subset mentioned in
993     # Config's ccdlflags and ldflags.  (Those are the choices of those who
994     # built the Perl installation, which are not necessarily appropriate
995     # for building PostgreSQL.)
996     ldopts = run_command(perl, '-MExtUtils::Embed', '-e', 'ldopts', check: true).stdout().strip()
997     undesired = run_command(perl_conf_cmd, 'ccdlflags', check: true).stdout().split()
998     undesired += run_command(perl_conf_cmd, 'ldflags', check: true).stdout().split()
1000     perl_ldopts = []
1001     foreach ldopt : ldopts.split(' ')
1002       if ldopt == '' or ldopt in undesired
1003         continue
1004       endif
1006       perl_ldopts += ldopt.strip('"')
1007     endforeach
1009     message('LDFLAGS recommended by perl: "@0@"'.format(ldopts))
1010     message('LDFLAGS for embedding perl: "@0@"'.format(' '.join(perl_ldopts)))
1012     perl_dep_int = declare_dependency(
1013       compile_args: perl_ccflags,
1014       link_args: perl_ldopts,
1015       version: perlversion,
1016     )
1018     # While we're at it, check that we can link to libperl.
1019     # On most platforms, if perl.h is there then libperl.so will be too, but
1020     # at this writing Debian packages them separately.
1021     perl_link_test = '''
1022 /* see plperl.h */
1023 #ifdef _MSC_VER
1024 #define __inline__ inline
1025 #endif
1026 #include <EXTERN.h>
1027 #include <perl.h>
1028 int main(void)
1030 perl_alloc();
1031 }'''
1032     if not cc.links(perl_link_test, name: 'libperl',
1033           args: test_c_args + perl_ccflags + perl_ldopts,
1034           include_directories: postgres_inc)
1035       perl_may_work = false
1036       perl_msg = 'missing libperl'
1037     endif
1039   endif # perl_may_work
1041   if perl_may_work
1042     perl_dep = perl_dep_int
1043   else
1044     if perlopt.enabled()
1045       error('dependency plperl failed: @0@'.format(perl_msg))
1046     else
1047       message('disabling optional dependency plperl: @0@'.format(perl_msg))
1048     endif
1049   endif
1050 endif
1054 ###############################################################
1055 # Library: Python (for plpython)
1056 ###############################################################
1058 pyopt = get_option('plpython')
1059 if not pyopt.disabled()
1060   pm = import('python')
1061   python3_inst = pm.find_installation(required: pyopt)
1062   python3_dep = python3_inst.dependency(embed: true, required: pyopt)
1063   if not cc.check_header('Python.h', dependencies: python3_dep, required: pyopt)
1064     python3_dep = not_found_dep
1065   endif
1066 else
1067   python3_dep = not_found_dep
1068 endif
1072 ###############################################################
1073 # Library: Readline
1074 ###############################################################
1076 if not get_option('readline').disabled()
1077   libedit_preferred = get_option('libedit_preferred')
1078   # Set the order of readline dependencies
1079   check_readline_deps = libedit_preferred ? \
1080     ['libedit', 'readline'] : ['readline', 'libedit']
1082   foreach readline_dep : check_readline_deps
1083     readline = dependency(readline_dep, required: false)
1084     if not readline.found()
1085       readline = cc.find_library(readline_dep,
1086         required: get_option('readline'),
1087         dirs: test_lib_d)
1088     endif
1089     if readline.found()
1090       break
1091     endif
1092   endforeach
1094   if readline.found()
1095     cdata.set('HAVE_LIBREADLINE', 1)
1097     editline_prefix = {
1098       'header_prefix': 'editline/',
1099       'flag_prefix': 'EDITLINE_',
1100     }
1101     readline_prefix = {
1102       'header_prefix': 'readline/',
1103       'flag_prefix': 'READLINE_',
1104     }
1105     default_prefix = {
1106       'header_prefix': '',
1107       'flag_prefix': '',
1108     }
1110     # Set the order of prefixes
1111     prefixes = libedit_preferred ? \
1112       [editline_prefix, default_prefix, readline_prefix] : \
1113       [readline_prefix, default_prefix, editline_prefix]
1115     at_least_one_header_found = false
1116     foreach header : ['history', 'readline']
1117       is_found = false
1118       foreach prefix : prefixes
1119         header_file = '@0@@1@.h'.format(prefix['header_prefix'], header)
1120         # Check history.h and readline.h
1121         if not is_found and cc.has_header(header_file,
1122             args: test_c_args, include_directories: postgres_inc,
1123             dependencies: [readline], required: false)
1124           if header == 'readline'
1125             readline_h = header_file
1126           endif
1127           cdata.set('HAVE_@0@@1@_H'.format(prefix['flag_prefix'], header).to_upper(), 1)
1128           is_found = true
1129           at_least_one_header_found = true
1130         endif
1131       endforeach
1132     endforeach
1134     if not at_least_one_header_found
1135       error('''readline header not found
1136 If you have @0@ already installed, see meson-log/meson-log.txt for details on the
1137 failure. It is possible the compiler isn't looking in the proper directory.
1138 Use -Dreadline=disabled to disable readline support.'''.format(readline_dep))
1139     endif
1141     check_funcs = [
1142       'append_history',
1143       'history_truncate_file',
1144       'rl_completion_matches',
1145       'rl_filename_completion_function',
1146       'rl_reset_screen_size',
1147       'rl_variable_bind',
1148     ]
1150     foreach func : check_funcs
1151       found = cc.has_function(func, dependencies: [readline],
1152         args: test_c_args, include_directories: postgres_inc)
1153       cdata.set('HAVE_' + func.to_upper(), found ? 1 : false)
1154     endforeach
1156     check_vars = [
1157       'rl_completion_suppress_quote',
1158       'rl_filename_quote_characters',
1159       'rl_filename_quoting_function',
1160     ]
1162     foreach var : check_vars
1163       cdata.set('HAVE_' + var.to_upper(),
1164         cc.has_header_symbol(readline_h, var,
1165           args: test_c_args, include_directories: postgres_inc,
1166           prefix: '#include <stdio.h>',
1167           dependencies: [readline]) ? 1 : false)
1168     endforeach
1170     # If found via cc.find_library() ensure headers are found when using the
1171     # dependency. On meson < 0.57 one cannot do compiler checks using the
1172     # dependency returned by declare_dependency(), so we can't do this above.
1173     if readline.type_name() == 'library'
1174       readline = declare_dependency(dependencies: readline,
1175         include_directories: postgres_inc)
1176     endif
1178     # On windows with mingw readline requires auto-import to successfully
1179     # link, as the headers don't use declspec(dllimport)
1180     if host_system == 'windows' and cc.get_id() != 'msvc'
1181       readline = declare_dependency(dependencies: readline,
1182         link_args: '-Wl,--enable-auto-import')
1183     endif
1184   endif
1186   # XXX: Figure out whether to implement mingw warning equivalent
1187 else
1188   readline = not_found_dep
1189 endif
1193 ###############################################################
1194 # Library: selinux
1195 ###############################################################
1197 selinux = not_found_dep
1198 selinuxopt = get_option('selinux')
1199 if meson.version().version_compare('>=0.59')
1200   selinuxopt = selinuxopt.disable_auto_if(host_system != 'linux')
1201 endif
1202 selinux = dependency('libselinux', required: selinuxopt, version: '>= 2.1.10')
1203 cdata.set('HAVE_LIBSELINUX',
1204   selinux.found() ? 1 : false)
1208 ###############################################################
1209 # Library: systemd
1210 ###############################################################
1212 systemd = not_found_dep
1213 systemdopt = get_option('systemd')
1214 if meson.version().version_compare('>=0.59')
1215   systemdopt = systemdopt.disable_auto_if(host_system != 'linux')
1216 endif
1217 systemd = dependency('libsystemd', required: systemdopt)
1218 cdata.set('USE_SYSTEMD', systemd.found() ? 1 : false)
1222 ###############################################################
1223 # Library: SSL
1224 ###############################################################
1226 ssl = not_found_dep
1227 ssl_library = 'none'
1228 sslopt = get_option('ssl')
1230 if sslopt == 'auto' and auto_features.disabled()
1231   sslopt = 'none'
1232 endif
1234 if sslopt in ['auto', 'openssl']
1235   openssl_required = (sslopt == 'openssl')
1237   # Try to find openssl via pkg-config et al, if that doesn't work
1238   # (e.g. because it's provided as part of the OS, like on FreeBSD), look for
1239   # the library names that we know about.
1241   # via pkg-config et al
1242   ssl = dependency('openssl', required: false)
1243   # only meson >= 0.57 supports declare_dependency() in cc.has_function(), so
1244   # we pass cc.find_library() results if necessary
1245   ssl_int = []
1247   # via library + headers
1248   if not ssl.found()
1249     ssl_lib = cc.find_library('ssl',
1250       dirs: test_lib_d,
1251       header_include_directories: postgres_inc,
1252       has_headers: ['openssl/ssl.h', 'openssl/err.h'],
1253       required: openssl_required)
1254     crypto_lib = cc.find_library('crypto',
1255       dirs: test_lib_d,
1256       required: openssl_required)
1257     if ssl_lib.found() and crypto_lib.found()
1258       ssl_int = [ssl_lib, crypto_lib]
1259       ssl = declare_dependency(dependencies: ssl_int, include_directories: postgres_inc)
1260     endif
1261   elif cc.has_header('openssl/ssl.h', args: test_c_args, dependencies: ssl, required: openssl_required) and \
1262        cc.has_header('openssl/err.h', args: test_c_args, dependencies: ssl, required: openssl_required)
1263     ssl_int = [ssl]
1264   else
1265     ssl = not_found_dep
1266   endif
1268   if ssl.found()
1269     check_funcs = [
1270       ['CRYPTO_new_ex_data', {'required': true}],
1271       ['SSL_new', {'required': true}],
1273       # Function introduced in OpenSSL 1.0.2, not in LibreSSL.
1274       ['SSL_CTX_set_cert_cb'],
1276       # Functions introduced in OpenSSL 1.1.0. We used to check for
1277       # OPENSSL_VERSION_NUMBER, but that didn't work with 1.1.0, because LibreSSL
1278       # defines OPENSSL_VERSION_NUMBER to claim version 2.0.0, even though it
1279       # doesn't have these OpenSSL 1.1.0 functions. So check for individual
1280       # functions.
1281       ['OPENSSL_init_ssl'],
1282       ['BIO_get_data'],
1283       ['BIO_meth_new'],
1284       ['ASN1_STRING_get0_data'],
1285       ['HMAC_CTX_new'],
1286       ['HMAC_CTX_free'],
1288       # OpenSSL versions before 1.1.0 required setting callback functions, for
1289       # thread-safety. In 1.1.0, it's no longer required, and CRYPTO_lock()
1290       # function was removed.
1291       ['CRYPTO_lock'],
1293       # Function introduced in OpenSSL 1.1.1
1294       ['X509_get_signature_info'],
1295     ]
1297     are_openssl_funcs_complete = true
1298     foreach c : check_funcs
1299       func = c.get(0)
1300       val = cc.has_function(func, args: test_c_args, dependencies: ssl_int)
1301       required = c.get(1, {}).get('required', false)
1302       if required and not val
1303         are_openssl_funcs_complete = false
1304         if openssl_required
1305           error('openssl function @0@ is required'.format(func))
1306         endif
1307         break
1308       elif not required
1309         cdata.set('HAVE_' + func.to_upper(), val ? 1 : false)
1310       endif
1311     endforeach
1313     if are_openssl_funcs_complete
1314       cdata.set('USE_OPENSSL', 1,
1315                 description: 'Define to 1 to build with OpenSSL support. (-Dssl=openssl)')
1316       cdata.set('OPENSSL_API_COMPAT', '0x10002000L',
1317                 description: 'Define to the OpenSSL API version in use. This avoids deprecation warnings from newer OpenSSL versions.')
1318       ssl_library = 'openssl'
1319     else
1320       ssl = not_found_dep
1321     endif
1322   endif
1323 endif
1325 if sslopt == 'auto' and auto_features.enabled() and not ssl.found()
1326   error('no SSL library found')
1327 endif
1331 ###############################################################
1332 # Library: uuid
1333 ###############################################################
1335 uuidopt = get_option('uuid')
1336 if uuidopt != 'none'
1337   uuidname = uuidopt.to_upper()
1338   if uuidopt == 'e2fs'
1339     uuid = dependency('uuid', required: true)
1340     uuidfunc = 'uuid_generate'
1341     uuidheader = 'uuid/uuid.h'
1342   elif uuidopt == 'bsd'
1343     # libc should have uuid function
1344     uuid = declare_dependency()
1345     uuidfunc = 'uuid_to_string'
1346     uuidheader = 'uuid.h'
1347   elif uuidopt == 'ossp'
1348     uuid = dependency('ossp-uuid', required: true)
1349     uuidfunc = 'uuid_export'
1350     uuidheader = 'uuid.h'
1351   else
1352     error('unknown uuid build option value: @0@'.format(uuidopt))
1353   endif
1355   if not cc.has_header_symbol(uuidheader, uuidfunc, args: test_c_args, dependencies: uuid)
1356     error('uuid library @0@ missing required function @1@'.format(uuidopt, uuidfunc))
1357   endif
1358   cdata.set('HAVE_@0@'.format(uuidheader.underscorify().to_upper()), 1)
1360   cdata.set('HAVE_UUID_@0@'.format(uuidname), 1,
1361            description: 'Define to 1 if you have @0@ UUID support.'.format(uuidname))
1362 else
1363   uuid = not_found_dep
1364 endif
1368 ###############################################################
1369 # Library: zlib
1370 ###############################################################
1372 zlibopt = get_option('zlib')
1373 zlib = not_found_dep
1374 if not zlibopt.disabled()
1375   zlib_t = dependency('zlib', required: zlibopt)
1377   if zlib_t.type_name() == 'internal'
1378     # if fallback was used, we don't need to test if headers are present (they
1379     # aren't built yet, so we can't test)
1380     zlib = zlib_t
1381   elif not zlib_t.found()
1382     warning('did not find zlib')
1383   elif not cc.has_header('zlib.h',
1384       args: test_c_args, include_directories: postgres_inc,
1385       dependencies: [zlib_t], required: zlibopt)
1386     warning('zlib header not found')
1387   elif not cc.has_type('z_streamp',
1388       dependencies: [zlib_t], prefix: '#include <zlib.h>',
1389       args: test_c_args, include_directories: postgres_inc)
1390     if zlibopt.enabled()
1391       error('zlib version is too old')
1392     else
1393       warning('zlib version is too old')
1394     endif
1395   else
1396     zlib = zlib_t
1397   endif
1399   if zlib.found()
1400     cdata.set('HAVE_LIBZ', 1)
1401   endif
1402 endif
1406 ###############################################################
1407 # Library: tap test dependencies
1408 ###############################################################
1410 # Check whether tap tests are enabled or not
1411 tap_tests_enabled = false
1412 tapopt = get_option('tap_tests')
1413 if not tapopt.disabled()
1414   # Checking for perl modules for tap tests
1415   perl_ipc_run_check = run_command(perl, 'config/check_modules.pl', check: false)
1416   if perl_ipc_run_check.returncode() != 0
1417     message(perl_ipc_run_check.stderr().strip())
1418     if tapopt.enabled()
1419       error('Additional Perl modules are required to run TAP tests.')
1420     else
1421       warning('Additional Perl modules are required to run TAP tests.')
1422     endif
1423   else
1424     tap_tests_enabled = true
1425   endif
1426 endif
1430 ###############################################################
1431 # Library: zstd
1432 ###############################################################
1434 zstdopt = get_option('zstd')
1435 if not zstdopt.disabled()
1436   zstd = dependency('libzstd', required: zstdopt, version: '>=1.4.0')
1438   if zstd.found()
1439     cdata.set('USE_ZSTD', 1)
1440     cdata.set('HAVE_LIBZSTD', 1)
1441   endif
1443 else
1444   zstd = not_found_dep
1445 endif
1449 ###############################################################
1450 # Compiler tests
1451 ###############################################################
1453 # Do we need -std=c99 to compile C99 code? We don't want to add -std=c99
1454 # unnecessarily, because we optionally rely on newer features.
1455 c99_test = '''
1456 #include <stdbool.h>
1457 #include <complex.h>
1458 #include <tgmath.h>
1459 #include <inttypes.h>
1461 struct named_init_test {
1462   int a;
1463   int b;
1466 extern void structfunc(struct named_init_test);
1468 int main(int argc, char **argv)
1470   struct named_init_test nit = {
1471     .a = 3,
1472     .b = 5,
1473   };
1475   for (int loop_var = 0; loop_var < 3; loop_var++)
1476   {
1477     nit.a += nit.b;
1478   }
1480   structfunc((struct named_init_test){1, 0});
1482   return nit.a != 0;
1486 if not cc.compiles(c99_test, name: 'c99', args: test_c_args)
1487   if cc.compiles(c99_test, name: 'c99 with -std=c99',
1488         args: test_c_args + ['-std=c99'])
1489     test_c_args += '-std=c99'
1490     cflags += '-std=c99'
1491   else
1492     error('C compiler does not support C99')
1493   endif
1494 endif
1496 sizeof_long = cc.sizeof('long', args: test_c_args)
1497 cdata.set('SIZEOF_LONG', sizeof_long)
1498 if sizeof_long == 8
1499   cdata.set('HAVE_LONG_INT_64', 1)
1500   cdata.set('PG_INT64_TYPE', 'long int')
1501   cdata.set_quoted('INT64_MODIFIER', 'l')
1502 elif sizeof_long == 4 and cc.sizeof('long long', args: test_c_args) == 8
1503   cdata.set('HAVE_LONG_LONG_INT_64', 1)
1504   cdata.set('PG_INT64_TYPE', 'long long int')
1505   cdata.set_quoted('INT64_MODIFIER', 'll')
1506 else
1507   error('do not know how to get a 64bit int')
1508 endif
1510 if host_machine.endian() == 'big'
1511   cdata.set('WORDS_BIGENDIAN', 1)
1512 endif
1514 alignof_types = ['short', 'int', 'long', 'double']
1515 maxalign = 0
1516 foreach t : alignof_types
1517   align = cc.alignment(t, args: test_c_args)
1518   if maxalign < align
1519     maxalign = align
1520   endif
1521   cdata.set('ALIGNOF_@0@'.format(t.to_upper()), align)
1522 endforeach
1523 cdata.set('MAXIMUM_ALIGNOF', maxalign)
1525 cdata.set('SIZEOF_VOID_P', cc.sizeof('void *', args: test_c_args))
1526 cdata.set('SIZEOF_SIZE_T', cc.sizeof('size_t', args: test_c_args))
1529 # Check if __int128 is a working 128 bit integer type, and if so
1530 # define PG_INT128_TYPE to that typename.
1532 # This currently only detects a GCC/clang extension, but support for other
1533 # environments may be added in the future.
1535 # For the moment we only test for support for 128bit math; support for
1536 # 128bit literals and snprintf is not required.
1537 if cc.links('''
1538   /*
1539    * We don't actually run this test, just link it to verify that any support
1540    * functions needed for __int128 are present.
1541    *
1542    * These are globals to discourage the compiler from folding all the
1543    * arithmetic tests down to compile-time constants.  We do not have
1544    * convenient support for 128bit literals at this point...
1545    */
1546   __int128 a = 48828125;
1547   __int128 b = 97656250;
1549   int main(void)
1550   {
1551       __int128 c,d;
1552       a = (a << 12) + 1; /* 200000000001 */
1553       b = (b << 12) + 5; /* 400000000005 */
1554       /* try the most relevant arithmetic ops */
1555       c = a * b;
1556       d = (c + b) / b;
1557       /* must use the results, else compiler may optimize arithmetic away */
1558       return d != a+1;
1559   }''',
1560   name: '__int128',
1561   args: test_c_args)
1563   buggy_int128 = false
1565   # Use of non-default alignment with __int128 tickles bugs in some compilers.
1566   # If not cross-compiling, we can test for bugs and disable use of __int128
1567   # with buggy compilers.  If cross-compiling, hope for the best.
1568   # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83925
1569   if not meson.is_cross_build()
1570     r = cc.run('''
1571     /* This must match the corresponding code in c.h: */
1572     #if defined(__GNUC__) || defined(__SUNPRO_C) || defined(__IBMC__)
1573     #define pg_attribute_aligned(a) __attribute__((aligned(a)))
1574     #elif defined(_MSC_VER)
1575     #define pg_attribute_aligned(a) __declspec(align(a))
1576     #endif
1577     typedef __int128 int128a
1578     #if defined(pg_attribute_aligned)
1579     pg_attribute_aligned(8)
1580     #endif
1581     ;
1583     int128a holder;
1584     void pass_by_val(void *buffer, int128a par) { holder = par; }
1586     int main(void)
1587     {
1588         long int i64 = 97656225L << 12;
1589         int128a q;
1590         pass_by_val(main, (int128a) i64);
1591         q = (int128a) i64;
1592         return q != holder;
1593     }''',
1594     name: '__int128 alignment bug',
1595     args: test_c_args)
1596     assert(r.compiled())
1597     if r.returncode() != 0
1598       buggy_int128 = true
1599       message('__int128 support present but buggy and thus disabled')
1600     endif
1601   endif
1603   if not buggy_int128
1604     cdata.set('PG_INT128_TYPE', '__int128')
1605     cdata.set('ALIGNOF_PG_INT128_TYPE', cc.alignment('__int128', args: test_c_args))
1606   endif
1607 endif
1610 # Check if the C compiler knows computed gotos (gcc extension, also
1611 # available in at least clang).  If so, define HAVE_COMPUTED_GOTO.
1613 # Checking whether computed gotos are supported syntax-wise ought to
1614 # be enough, as the syntax is otherwise illegal.
1615 if cc.compiles('''
1616     static inline int foo(void)
1617     {
1618       void *labeladdrs[] = {&&my_label};
1619       goto *labeladdrs[0];
1620       my_label:
1621       return 1;
1622     }''',
1623     args: test_c_args)
1624   cdata.set('HAVE_COMPUTED_GOTO', 1)
1625 endif
1628 # Check if the C compiler understands _Static_assert(),
1629 # and define HAVE__STATIC_ASSERT if so.
1631 # We actually check the syntax ({ _Static_assert(...) }), because we need
1632 # gcc-style compound expressions to be able to wrap the thing into macros.
1633 if cc.compiles('''
1634     int main(int arg, char **argv)
1635     {
1636         ({ _Static_assert(1, "foo"); });
1637     }
1638     ''',
1639     args: test_c_args)
1640   cdata.set('HAVE__STATIC_ASSERT', 1)
1641 endif
1644 # We use <stdbool.h> if we have it and it declares type bool as having
1645 # size 1.  Otherwise, c.h will fall back to declaring bool as unsigned char.
1646 if cc.has_type('_Bool', args: test_c_args) \
1647     and cc.has_type('bool', prefix: '#include <stdbool.h>', args: test_c_args) \
1648     and cc.sizeof('bool', prefix: '#include <stdbool.h>', args: test_c_args) == 1
1649   cdata.set('HAVE__BOOL', 1)
1650   cdata.set('PG_USE_STDBOOL', 1)
1651 endif
1654 # Need to check a call with %m because netbsd supports gnu_printf but emits a
1655 # warning for each use of %m.
1656 printf_attributes = ['gnu_printf', '__syslog__', 'printf']
1657 testsrc = '''
1658 extern void emit_log(int ignore, const char *fmt,...) __attribute__((format(@0@, 2,3)));
1659 static void call_log(void)
1661     emit_log(0, "error: %s: %m", "foo");
1664 attrib_error_args = cc.get_supported_arguments('-Werror=format', '-Werror=ignored-attributes')
1665 foreach a : printf_attributes
1666   if cc.compiles(testsrc.format(a),
1667       args: test_c_args + attrib_error_args, name: 'format ' + a)
1668     cdata.set('PG_PRINTF_ATTRIBUTE', a)
1669     break
1670   endif
1671 endforeach
1674 if cc.has_function_attribute('visibility:default') and \
1675     cc.has_function_attribute('visibility:hidden')
1676   cdata.set('HAVE_VISIBILITY_ATTRIBUTE', 1)
1678   # Only newer versions of meson know not to apply gnu_symbol_visibility =
1679   # inlineshidden to C code as well... And either way, we want to put these
1680   # flags into exported files (pgxs, .pc files).
1681   cflags_mod += '-fvisibility=hidden'
1682   cxxflags_mod += ['-fvisibility=hidden', '-fvisibility-inlines-hidden']
1683   ldflags_mod += '-fvisibility=hidden'
1684 endif
1687 # Check if various builtins exist. Some builtins are tested separately,
1688 # because we want to test something more complicated than the generic case.
1689 builtins = [
1690   'bswap16',
1691   'bswap32',
1692   'bswap64',
1693   'clz',
1694   'ctz',
1695   'constant_p',
1696   'frame_address',
1697   'popcount',
1698   'unreachable',
1701 foreach builtin : builtins
1702   fname = '__builtin_@0@'.format(builtin)
1703   if cc.has_function(fname, args: test_c_args)
1704     cdata.set('HAVE@0@'.format(fname.to_upper()), 1)
1705   endif
1706 endforeach
1709 # Check if the C compiler understands __builtin_types_compatible_p,
1710 # and define HAVE__BUILTIN_TYPES_COMPATIBLE_P if so.
1712 # We check usage with __typeof__, though it's unlikely any compiler would
1713 # have the former and not the latter.
1714 if cc.compiles('''
1715     static int x;
1716     static int y[__builtin_types_compatible_p(__typeof__(x), int)];
1717     ''',
1718     name: '__builtin_types_compatible_p',
1719     args: test_c_args)
1720   cdata.set('HAVE__BUILTIN_TYPES_COMPATIBLE_P', 1)
1721 endif
1724 # Check if the C compiler understands __builtin_$op_overflow(),
1725 # and define HAVE__BUILTIN_OP_OVERFLOW if so.
1727 # Check for the most complicated case, 64 bit multiplication, as a
1728 # proxy for all of the operations.  To detect the case where the compiler
1729 # knows the function but library support is missing, we must link not just
1730 # compile, and store the results in global variables so the compiler doesn't
1731 # optimize away the call.
1732 if cc.links('''
1733     INT64 a = 1;
1734     INT64 b = 1;
1735     INT64 result;
1737     int main(void)
1738     {
1739         return __builtin_mul_overflow(a, b, &result);
1740     }''',
1741     name: '__builtin_mul_overflow',
1742     args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))],
1743     )
1744   cdata.set('HAVE__BUILTIN_OP_OVERFLOW', 1)
1745 endif
1748 # XXX: The configure.ac check for __cpuid() is broken, we don't copy that
1749 # here. To prevent problems due to two detection methods working, stop
1750 # checking after one.
1751 if cc.links('''
1752     #include <cpuid.h>
1753     int main(int arg, char **argv)
1754     {
1755         unsigned int exx[4] = {0, 0, 0, 0};
1756         __get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
1757     }
1758     ''', name: '__get_cpuid',
1759     args: test_c_args)
1760   cdata.set('HAVE__GET_CPUID', 1)
1761 elif cc.links('''
1762     #include <intrin.h>
1763     int main(int arg, char **argv)
1764     {
1765         unsigned int exx[4] = {0, 0, 0, 0};
1766         __cpuid(exx, 1);
1767     }
1768     ''', name: '__cpuid',
1769     args: test_c_args)
1770   cdata.set('HAVE__CPUID', 1)
1771 endif
1774 # Defend against clang being used on x86-32 without SSE2 enabled.  As current
1775 # versions of clang do not understand -fexcess-precision=standard, the use of
1776 # x87 floating point operations leads to problems like isinf possibly returning
1777 # false for a value that is infinite when converted from the 80bit register to
1778 # the 8byte memory representation.
1780 # Only perform the test if the compiler doesn't understand
1781 # -fexcess-precision=standard, that way a potentially fixed compiler will work
1782 # automatically.
1783 if '-fexcess-precision=standard' not in cflags
1784   if not cc.compiles('''
1785 #if defined(__clang__) && defined(__i386__) && !defined(__SSE2_MATH__)
1786 choke me
1787 #endif''',
1788       name: '', args: test_c_args)
1789     error('Compiling PostgreSQL with clang, on 32bit x86, requires SSE2 support. Use -msse2 or use gcc.')
1790   endif
1791 endif
1795 ###############################################################
1796 # Compiler flags
1797 ###############################################################
1799 common_functional_flags = [
1800   # Disable strict-aliasing rules; needed for gcc 3.3+
1801   '-fno-strict-aliasing',
1802   # Disable optimizations that assume no overflow; needed for gcc 4.3+
1803   '-fwrapv',
1804   '-fexcess-precision=standard',
1807 cflags += cc.get_supported_arguments(common_functional_flags)
1808 if llvm.found()
1809   cxxflags += cpp.get_supported_arguments(common_functional_flags)
1810 endif
1812 vectorize_cflags = cc.get_supported_arguments(['-ftree-vectorize'])
1813 unroll_loops_cflags = cc.get_supported_arguments(['-funroll-loops'])
1815 common_warning_flags = [
1816   '-Wmissing-prototypes',
1817   '-Wpointer-arith',
1818   # Really don't want VLAs to be used in our dialect of C
1819   '-Werror=vla',
1820   # On macOS, complain about usage of symbols newer than the deployment target
1821   '-Werror=unguarded-availability-new',
1822   '-Wendif-labels',
1823   '-Wmissing-format-attribute',
1824   '-Wimplicit-fallthrough=3',
1825   '-Wcast-function-type',
1826   '-Wshadow=compatible-local',
1827   # This was included in -Wall/-Wformat in older GCC versions
1828   '-Wformat-security',
1831 cflags_warn += cc.get_supported_arguments(common_warning_flags)
1832 if llvm.found()
1833   cxxflags_warn += cpp.get_supported_arguments(common_warning_flags)
1834 endif
1836 # A few places with imported code get a pass on -Wdeclaration-after-statement, remember
1837 # the result for them
1838 cflags_no_decl_after_statement = []
1839 if cc.has_argument('-Wdeclaration-after-statement')
1840   cflags_warn += '-Wdeclaration-after-statement'
1841   cflags_no_decl_after_statement += '-Wno-declaration-after-statement'
1842 endif
1845 # The following tests want to suppress various unhelpful warnings by adding
1846 # -Wno-foo switches.  But gcc won't complain about unrecognized -Wno-foo
1847 # switches, so we have to test for the positive form and if that works,
1848 # add the negative form.
1850 negative_warning_flags = [
1851   # Suppress clang's unhelpful unused-command-line-argument warnings.
1852   'unused-command-line-argument',
1854   # Remove clang 12+'s compound-token-split-by-macro, as this causes a lot
1855   # of warnings when building plperl because of usages in the Perl headers.
1856   'compound-token-split-by-macro',
1858   # Similarly disable useless truncation warnings from gcc 8+
1859   'format-truncation',
1860   'stringop-truncation',
1862   # Suppress clang 16's strict warnings about function casts
1863   'cast-function-type-strict',
1865   # To make warning_level=2 / -Wextra work, we'd need at least the following
1866   # 'clobbered',
1867   # 'missing-field-initializers',
1868   # 'sign-compare',
1869   # 'unused-parameter',
1872 foreach w : negative_warning_flags
1873   if cc.has_argument('-W' + w)
1874     cflags_warn += '-Wno-' + w
1875   endif
1876   if llvm.found() and cpp.has_argument('-W' + w)
1877     cxxflags_warn += '-Wno-' + w
1878   endif
1879 endforeach
1882 # From Project.pm
1883 if cc.get_id() == 'msvc'
1884   cflags_warn += [
1885     '/wd4018', # signed/unsigned mismatch
1886     '/wd4244', # conversion from 'type1' to 'type2', possible loss of data
1887     '/wd4273', # inconsistent DLL linkage
1888     '/wd4101', # unreferenced local variable
1889     '/wd4102', # unreferenced label
1890     '/wd4090', # different 'modifier' qualifiers
1891     '/wd4267', # conversion from 'size_t' to 'type', possible loss of data
1892   ]
1894   cppflags += [
1895     '/DWIN32',
1896     '/DWINDOWS',
1897     '/D__WINDOWS__',
1898     '/D__WIN32__',
1899     '/D_CRT_SECURE_NO_DEPRECATE',
1900     '/D_CRT_NONSTDC_NO_DEPRECATE',
1901   ]
1903   # We never need export libraries. As link.exe reports their creation, they
1904   # are unnecessarily noisy. Similarly, we don't need import library for
1905   # modules, we only import them dynamically, and they're also noisy.
1906   ldflags += '/NOEXP'
1907   ldflags_mod += '/NOIMPLIB'
1908 endif
1912 ###############################################################
1913 # Atomics
1914 ###############################################################
1916 if not get_option('spinlocks')
1917   warning('Not using spinlocks will cause poor performance')
1918 else
1919   cdata.set('HAVE_SPINLOCKS', 1)
1920 endif
1922 if not get_option('atomics')
1923   warning('Not using atomics will cause poor performance')
1924 else
1925   # XXX: perhaps we should require some atomics support in this case these
1926   # days?
1927   cdata.set('HAVE_ATOMICS', 1)
1929   atomic_checks = [
1930     {'name': 'HAVE_GCC__SYNC_CHAR_TAS',
1931      'desc': '__sync_lock_test_and_set(char)',
1932      'test': '''
1933 char lock = 0;
1934 __sync_lock_test_and_set(&lock, 1);
1935 __sync_lock_release(&lock);'''},
1937     {'name': 'HAVE_GCC__SYNC_INT32_TAS',
1938      'desc': '__sync_lock_test_and_set(int32)',
1939      'test': '''
1940 int lock = 0;
1941 __sync_lock_test_and_set(&lock, 1);
1942 __sync_lock_release(&lock);'''},
1944     {'name': 'HAVE_GCC__SYNC_INT32_CAS',
1945      'desc': '__sync_val_compare_and_swap(int32)',
1946      'test': '''
1947 int val = 0;
1948 __sync_val_compare_and_swap(&val, 0, 37);'''},
1950     {'name': 'HAVE_GCC__SYNC_INT64_CAS',
1951      'desc': '__sync_val_compare_and_swap(int64)',
1952      'test': '''
1953 INT64 val = 0;
1954 __sync_val_compare_and_swap(&val, 0, 37);'''},
1956     {'name': 'HAVE_GCC__ATOMIC_INT32_CAS',
1957      'desc': ' __atomic_compare_exchange_n(int32)',
1958      'test': '''
1959 int val = 0;
1960 int expect = 0;
1961 __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);'''},
1963     {'name': 'HAVE_GCC__ATOMIC_INT64_CAS',
1964      'desc': ' __atomic_compare_exchange_n(int64)',
1965      'test': '''
1966 INT64 val = 0;
1967 INT64 expect = 0;
1968 __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);'''},
1969   ]
1971   foreach check : atomic_checks
1972     test = '''
1973 int main(void)
1976 }'''.format(check['test'])
1978     cdata.set(check['name'],
1979       cc.links(test,
1980         name: check['desc'],
1981         args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))]) ? 1 : false
1982     )
1983   endforeach
1985 endif
1989 ###############################################################
1990 # Select CRC-32C implementation.
1992 # If we are targeting a processor that has Intel SSE 4.2 instructions, we can
1993 # use the special CRC instructions for calculating CRC-32C. If we're not
1994 # targeting such a processor, but we can nevertheless produce code that uses
1995 # the SSE intrinsics, perhaps with some extra CFLAGS, compile both
1996 # implementations and select which one to use at runtime, depending on whether
1997 # SSE 4.2 is supported by the processor we're running on.
1999 # Similarly, if we are targeting an ARM processor that has the CRC
2000 # instructions that are part of the ARMv8 CRC Extension, use them. And if
2001 # we're not targeting such a processor, but can nevertheless produce code that
2002 # uses the CRC instructions, compile both, and select at runtime.
2003 ###############################################################
2005 have_optimized_crc = false
2006 cflags_crc = []
2007 if host_cpu == 'x86' or host_cpu == 'x86_64'
2009   if cc.get_id() == 'msvc'
2010     cdata.set('USE_SSE42_CRC32C', false)
2011     cdata.set('USE_SSE42_CRC32C_WITH_RUNTIME_CHECK', 1)
2012     have_optimized_crc = true
2013   else
2015     prog = '''
2016 #include <nmmintrin.h>
2018 int main(void)
2020     unsigned int crc = 0;
2021     crc = _mm_crc32_u8(crc, 0);
2022     crc = _mm_crc32_u32(crc, 0);
2023     /* return computed value, to prevent the above being optimized away */
2024     return crc == 0;
2028     if cc.links(prog, name: '_mm_crc32_u8 and _mm_crc32_u32 without -msse4.2',
2029           args: test_c_args)
2030       # Use Intel SSE 4.2 unconditionally.
2031       cdata.set('USE_SSE42_CRC32C', 1)
2032       have_optimized_crc = true
2033     elif cc.links(prog, name: '_mm_crc32_u8 and _mm_crc32_u32 with -msse4.2',
2034           args: test_c_args + ['-msse4.2'])
2035       # Use Intel SSE 4.2, with runtime check. The CPUID instruction is needed for
2036       # the runtime check.
2037       cflags_crc += '-msse4.2'
2038       cdata.set('USE_SSE42_CRC32C', false)
2039       cdata.set('USE_SSE42_CRC32C_WITH_RUNTIME_CHECK', 1)
2040       have_optimized_crc = true
2041     endif
2043   endif
2045 elif host_cpu == 'arm' or host_cpu == 'aarch64'
2047   prog = '''
2048 #include <arm_acle.h>
2050 int main(void)
2052     unsigned int crc = 0;
2053     crc = __crc32cb(crc, 0);
2054     crc = __crc32ch(crc, 0);
2055     crc = __crc32cw(crc, 0);
2056     crc = __crc32cd(crc, 0);
2058     /* return computed value, to prevent the above being optimized away */
2059     return crc == 0;
2063   if cc.links(prog, name: '__crc32cb, __crc32ch, __crc32cw, and __crc32cd without -march=armv8-a+crc',
2064       args: test_c_args)
2065     # Use ARM CRC Extension unconditionally
2066     cdata.set('USE_ARMV8_CRC32C', 1)
2067     have_optimized_crc = true
2068   elif cc.links(prog, name: '__crc32cb, __crc32ch, __crc32cw, and __crc32cd with -march=armv8-a+crc',
2069       args: test_c_args + ['-march=armv8-a+crc'])
2070     # Use ARM CRC Extension, with runtime check
2071     cflags_crc += '-march=armv8-a+crc'
2072     cdata.set('USE_ARMV8_CRC32C', false)
2073     cdata.set('USE_ARMV8_CRC32C_WITH_RUNTIME_CHECK', 1)
2074     have_optimized_crc = true
2075   endif
2076 endif
2078 if not have_optimized_crc
2079   # fall back to slicing-by-8 algorithm, which doesn't require any special CPU
2080   # support.
2081   cdata.set('USE_SLICING_BY_8_CRC32C', 1)
2082 endif
2086 ###############################################################
2087 # Other CPU specific stuff
2088 ###############################################################
2090 if host_cpu == 'x86_64'
2092   if cc.compiles('''
2093       void main(void)
2094       {
2095           long long x = 1; long long r;
2096           __asm__ __volatile__ (" popcntq %1,%0\n" : "=q"(r) : "rm"(x));
2097       }''',
2098       name: '@0@: popcntq instruction'.format(host_cpu),
2099       args: test_c_args)
2100     cdata.set('HAVE_X86_64_POPCNTQ', 1)
2101   endif
2103 elif host_cpu == 'ppc' or host_cpu == 'ppc64'
2104   # Check if compiler accepts "i"(x) when __builtin_constant_p(x).
2105   if cdata.has('HAVE__BUILTIN_CONSTANT_P')
2106     if cc.compiles('''
2107       static inline int
2108       addi(int ra, int si)
2109       {
2110           int res = 0;
2111           if (__builtin_constant_p(si))
2112               __asm__ __volatile__(
2113                   " addi %0,%1,%2\n" : "=r"(res) : "b"(ra), "i"(si));
2114           return res;
2115       }
2116       int test_adds(int x) { return addi(3, x) + addi(x, 5); }
2117       ''',
2118       args: test_c_args)
2119       cdata.set('HAVE_I_CONSTRAINT__BUILTIN_CONSTANT_P', 1)
2120     endif
2121   endif
2122 endif
2126 ###############################################################
2127 # Library / OS tests
2128 ###############################################################
2130 # XXX: Might be worth conditioning some checks on the OS, to avoid doing
2131 # unnecessary checks over and over, particularly on windows.
2132 header_checks = [
2133   'atomic.h',
2134   'copyfile.h',
2135   'crtdefs.h',
2136   'execinfo.h',
2137   'getopt.h',
2138   'ifaddrs.h',
2139   'langinfo.h',
2140   'mbarrier.h',
2141   'stdbool.h',
2142   'strings.h',
2143   'sys/epoll.h',
2144   'sys/event.h',
2145   'sys/personality.h',
2146   'sys/prctl.h',
2147   'sys/procctl.h',
2148   'sys/signalfd.h',
2149   'sys/ucred.h',
2150   'termios.h',
2151   'ucred.h',
2154 foreach header : header_checks
2155   varname = 'HAVE_' + header.underscorify().to_upper()
2157   # Emulate autoconf behaviour of not-found->undef, found->1
2158   found = cc.has_header(header,
2159     include_directories: postgres_inc, args: test_c_args)
2160   cdata.set(varname, found ? 1 : false,
2161             description: 'Define to 1 if you have the <@0@> header file.'.format(header))
2162 endforeach
2165 decl_checks = [
2166   ['F_FULLFSYNC', 'fcntl.h'],
2167   ['fdatasync', 'unistd.h'],
2168   ['posix_fadvise', 'fcntl.h'],
2169   ['strlcat', 'string.h'],
2170   ['strlcpy', 'string.h'],
2171   ['strnlen', 'string.h'],
2174 # Need to check for function declarations for these functions, because
2175 # checking for library symbols wouldn't handle deployment target
2176 # restrictions on macOS
2177 decl_checks += [
2178   ['preadv', 'sys/uio.h'],
2179   ['pwritev', 'sys/uio.h'],
2182 foreach c : decl_checks
2183   func = c.get(0)
2184   header = c.get(1)
2185   args = c.get(2, {})
2186   varname = 'HAVE_DECL_' + func.underscorify().to_upper()
2188   found = cc.has_header_symbol(header, func,
2189     args: test_c_args, include_directories: postgres_inc,
2190     kwargs: args)
2191   cdata.set10(varname, found, description:
2192 '''Define to 1 if you have the declaration of `@0@', and to 0 if you
2193    don't.'''.format(func))
2194 endforeach
2197 if cc.has_type('struct option',
2198     args: test_c_args, include_directories: postgres_inc,
2199     prefix: '@0@'.format(cdata.get('HAVE_GETOPT_H')) == '1' ? '#include <getopt.h>' : '')
2200   cdata.set('HAVE_STRUCT_OPTION', 1)
2201 endif
2204 foreach c : ['opterr', 'optreset']
2205   varname = 'HAVE_INT_' + c.underscorify().to_upper()
2207   if cc.links('''
2208 #include <unistd.h>
2209 int main(void)
2211     extern int @0@;
2212     @0@ = 1;
2214 '''.format(c), name: c, args: test_c_args)
2215     cdata.set(varname, 1)
2216   else
2217     cdata.set(varname, false)
2218   endif
2219 endforeach
2221 if cc.has_type('socklen_t',
2222     args: test_c_args, include_directories: postgres_inc,
2223     prefix: '''
2224 #include <sys/socket.h>''')
2225   cdata.set('HAVE_SOCKLEN_T', 1)
2226 endif
2228 if cc.has_member('struct sockaddr', 'sa_len',
2229     args: test_c_args, include_directories: postgres_inc,
2230     prefix: '''
2231 #include <sys/types.h>
2232 #include <sys/socket.h>''')
2233   cdata.set('HAVE_STRUCT_SOCKADDR_SA_LEN', 1)
2234 endif
2236 if cc.has_member('struct tm', 'tm_zone',
2237     args: test_c_args, include_directories: postgres_inc,
2238     prefix: '''
2239 #include <sys/types.h>
2240 #include <time.h>
2241 ''')
2242   cdata.set('HAVE_STRUCT_TM_TM_ZONE', 1)
2243 endif
2245 if cc.compiles('''
2246 #include <time.h>
2247 extern int foo(void);
2248 int foo(void)
2250     return timezone / 60;
2252 ''',
2253     name: 'global variable `timezone\' exists',
2254     args: test_c_args, include_directories: postgres_inc)
2255   cdata.set('HAVE_INT_TIMEZONE', 1)
2256 else
2257   cdata.set('HAVE_INT_TIMEZONE', false)
2258 endif
2260 if cc.has_type('union semun',
2261     args: test_c_args,
2262     include_directories: postgres_inc,
2263     prefix: '''
2264 #include <sys/types.h>
2265 #include <sys/ipc.h>
2266 #include <sys/sem.h>
2267 ''')
2268   cdata.set('HAVE_UNION_SEMUN', 1)
2269 endif
2271 if cc.compiles('''
2272 #include <string.h>
2273 int main(void)
2275   char buf[100];
2276   switch (strerror_r(1, buf, sizeof(buf)))
2277   { case 0: break; default: break; }
2278 }''',
2279     name: 'strerror_r',
2280     args: test_c_args, include_directories: postgres_inc)
2281   cdata.set('STRERROR_R_INT', 1)
2282 else
2283   cdata.set('STRERROR_R_INT', false)
2284 endif
2286 # Find the right header file for the locale_t type.  macOS needs xlocale.h;
2287 # standard is locale.h, but glibc <= 2.25 also had an xlocale.h file that
2288 # we should not use so we check the standard header first.  MSVC has a
2289 # replacement defined in src/include/port/win32_port.h.
2290 if not cc.has_type('locale_t', prefix: '#include <locale.h>') and \
2291    cc.has_type('locale_t', prefix: '#include <xlocale.h>')
2292   cdata.set('LOCALE_T_IN_XLOCALE', 1)
2293 endif
2295 # Check if the C compiler understands typeof or a variant.  Define
2296 # HAVE_TYPEOF if so, and define 'typeof' to the actual key word.
2297 foreach kw : ['typeof', '__typeof__', 'decltype']
2298   if cc.compiles('''
2299 int main(void)
2301     int x = 0;
2302     @0@(x) y;
2303     y = x;
2304     return y;
2306 '''.format(kw),
2307     name: 'typeof()',
2308     args: test_c_args, include_directories: postgres_inc)
2310     cdata.set('HAVE_TYPEOF', 1)
2311     if kw != 'typeof'
2312       cdata.set('typeof', kw)
2313     endif
2315     break
2316   endif
2317 endforeach
2320 # Try to find a declaration for wcstombs_l().  It might be in stdlib.h
2321 # (following the POSIX requirement for wcstombs()), or in locale.h, or in
2322 # xlocale.h.  If it's in the latter, define WCSTOMBS_L_IN_XLOCALE.
2323 wcstombs_l_test = '''
2324 #include <stdlib.h>
2325 #include <locale.h>
2328 void main(void)
2330 #ifndef wcstombs_l
2331     (void) wcstombs_l;
2332 #endif
2335 if (not cc.compiles(wcstombs_l_test.format(''),
2336       name: 'wcstombs_l') and
2337     cc.compiles(wcstombs_l_test.format('#include <xlocale.h>'),
2338       name: 'wcstombs_l in xlocale.h'))
2339     cdata.set('WCSTOMBS_L_IN_XLOCALE', 1)
2340 endif
2343 # MSVC doesn't cope well with defining restrict to __restrict, the spelling it
2344 # understands, because it conflicts with __declspec(restrict). Therefore we
2345 # define pg_restrict to the appropriate definition, which presumably won't
2346 # conflict.
2348 # We assume C99 support, so we don't need to make this conditional.
2350 # XXX: Historically we allowed platforms to disable restrict in template
2351 # files, but that was only added for AIX when building with XLC, which we
2352 # don't support yet.
2353 cdata.set('pg_restrict', '__restrict')
2356 # Most libraries are included only if they demonstrably provide a function we
2357 # need, but libm is an exception: always include it, because there are too
2358 # many compilers that play cute optimization games that will break probes for
2359 # standard functions such as pow().
2360 os_deps += cc.find_library('m', required: false)
2362 rt_dep = cc.find_library('rt', required: false)
2364 dl_dep = cc.find_library('dl', required: false)
2366 util_dep = cc.find_library('util', required: false)
2367 posix4_dep = cc.find_library('posix4', required: false)
2369 getopt_dep = cc.find_library('getopt', required: false)
2370 gnugetopt_dep = cc.find_library('gnugetopt', required: false)
2371 # Check if we want to replace getopt/getopt_long even if provided by the system
2372 # - Mingw has adopted a GNU-centric interpretation of optind/optreset,
2373 #   so always use our version on Windows
2374 # - On OpenBSD and Solaris, getopt() doesn't do what we want for long options
2375 #   (i.e., allow '-' as a flag character), so use our version on those platforms
2376 # - We want to use system's getopt_long() only if the system provides struct
2377 #   option
2378 always_replace_getopt = host_system in ['windows', 'cygwin', 'openbsd', 'solaris']
2379 always_replace_getopt_long = host_system in ['windows', 'cygwin'] or not cdata.has('HAVE_STRUCT_OPTION')
2381 # Required on BSDs
2382 execinfo_dep = cc.find_library('execinfo', required: false)
2384 if host_system == 'cygwin'
2385   cygipc_dep = cc.find_library('cygipc', required: false)
2386 else
2387   cygipc_dep = not_found_dep
2388 endif
2390 if host_system == 'sunos'
2391   socket_dep = cc.find_library('socket', required: false)
2392 else
2393   socket_dep = not_found_dep
2394 endif
2396 # XXX: Might be worth conditioning some checks on the OS, to avoid doing
2397 # unnecessary checks over and over, particularly on windows.
2398 func_checks = [
2399   ['_configthreadlocale', {'skip': host_system != 'windows'}],
2400   ['backtrace_symbols', {'dependencies': [execinfo_dep]}],
2401   ['clock_gettime', {'dependencies': [rt_dep, posix4_dep], 'define': false}],
2402   ['copyfile'],
2403   # gcc/clang's sanitizer helper library provides dlopen but not dlsym, thus
2404   # when enabling asan the dlopen check doesn't notice that -ldl is actually
2405   # required. Just checking for dlsym() ought to suffice.
2406   ['dlsym', {'dependencies': [dl_dep], 'define': false}],
2407   ['explicit_bzero'],
2408   ['fdatasync', {'dependencies': [rt_dep, posix4_dep], 'define': false}], # Solaris
2409   ['getifaddrs'],
2410   ['getopt', {'dependencies': [getopt_dep, gnugetopt_dep], 'skip': always_replace_getopt}],
2411   ['getopt_long', {'dependencies': [getopt_dep, gnugetopt_dep], 'skip': always_replace_getopt_long}],
2412   ['getpeereid'],
2413   ['getpeerucred'],
2414   ['inet_aton'],
2415   ['inet_pton'],
2416   ['kqueue'],
2417   ['mbstowcs_l'],
2418   ['memset_s'],
2419   ['mkdtemp'],
2420   ['posix_fadvise'],
2421   ['posix_fallocate'],
2422   ['ppoll'],
2423   ['pstat'],
2424   ['pthread_barrier_wait', {'dependencies': [thread_dep]}],
2425   ['pthread_is_threaded_np', {'dependencies': [thread_dep]}],
2426   ['sem_init', {'dependencies': [rt_dep, thread_dep], 'skip': sema_kind != 'unnamed_posix', 'define': false}],
2427   ['setproctitle', {'dependencies': [util_dep]}],
2428   ['setproctitle_fast'],
2429   ['shm_open', {'dependencies': [rt_dep], 'define': false}],
2430   ['shm_unlink', {'dependencies': [rt_dep], 'define': false}],
2431   ['shmget', {'dependencies': [cygipc_dep], 'define': false}],
2432   ['socket', {'dependencies': [socket_dep], 'define': false}],
2433   ['strchrnul'],
2434   ['strerror_r', {'dependencies': [thread_dep]}],
2435   ['strlcat'],
2436   ['strlcpy'],
2437   ['strnlen'],
2438   ['strsignal'],
2439   ['sync_file_range'],
2440   ['syncfs'],
2441   ['uselocale'],
2442   ['wcstombs_l'],
2445 func_check_results = {}
2446 foreach c : func_checks
2447   func = c.get(0)
2448   kwargs = c.get(1, {})
2449   deps = kwargs.get('dependencies', [])
2451   if kwargs.get('skip', false)
2452     continue
2453   endif
2455   found = cc.has_function(func, args: test_c_args)
2457   if not found
2458     foreach dep : deps
2459       if not dep.found()
2460         continue
2461       endif
2462       found = cc.has_function(func, args: test_c_args,
2463                               dependencies: [dep])
2464       if found
2465         os_deps += dep
2466         break
2467       endif
2468     endforeach
2469   endif
2471   func_check_results += {func: found}
2473   if kwargs.get('define', true)
2474     # Emulate autoconf behaviour of not-found->undef, found->1
2475     cdata.set('HAVE_' + func.underscorify().to_upper(),
2476               found  ? 1 : false,
2477               description: 'Define to 1 if you have the `@0@\' function.'.format(func))
2478   endif
2479 endforeach
2482 if cc.has_function('syslog', args: test_c_args) and \
2483     cc.check_header('syslog.h', args: test_c_args)
2484   cdata.set('HAVE_SYSLOG', 1)
2485 endif
2488 # if prerequisites for unnamed posix semas aren't fulfilled, fall back to sysv
2489 # semaphores
2490 if sema_kind == 'unnamed_posix' and \
2491    not func_check_results.get('sem_init', false)
2492   sema_kind = 'sysv'
2493 endif
2495 cdata.set('USE_@0@_SHARED_MEMORY'.format(shmem_kind.to_upper()), 1)
2496 cdata.set('USE_@0@_SEMAPHORES'.format(sema_kind.to_upper()), 1)
2498 cdata.set('MEMSET_LOOP_LIMIT', memset_loop_limit)
2499 cdata.set_quoted('DLSUFFIX', dlsuffix)
2502 # built later than the rest of the version metadata, we need SIZEOF_VOID_P
2503 cdata.set_quoted('PG_VERSION_STR',
2504   'PostgreSQL @0@ on @1@-@2@, compiled by @3@-@4@, @5@-bit'.format(
2505     pg_version, host_machine.cpu_family(), host_system,
2506     cc.get_id(), cc.version(), cdata.get('SIZEOF_VOID_P') * 8,
2507   )
2511 ###############################################################
2512 # NLS / Gettext
2513 ###############################################################
2515 nlsopt = get_option('nls')
2516 libintl = not_found_dep
2518 if not nlsopt.disabled()
2519   # otherwise there'd be lots of
2520   # "Gettext not found, all translation (po) targets will be ignored."
2521   # warnings if not found.
2522   msgfmt = find_program('msgfmt', required: nlsopt, native: true)
2524   # meson 0.59 has this wrapped in dependency('intl')
2525   if (msgfmt.found() and
2526       cc.check_header('libintl.h', required: nlsopt,
2527         args: test_c_args, include_directories: postgres_inc))
2529     # in libc
2530     if cc.has_function('ngettext')
2531       libintl = declare_dependency()
2532     else
2533       libintl = cc.find_library('intl',
2534         has_headers: ['libintl.h'], required: nlsopt,
2535         header_include_directories: postgres_inc,
2536         dirs: test_lib_d)
2537     endif
2538   endif
2540   if libintl.found()
2541     i18n = import('i18n')
2542     cdata.set('ENABLE_NLS', 1)
2543   endif
2544 endif
2548 ###############################################################
2549 # Build
2550 ###############################################################
2552 # Set up compiler / linker arguments to be used everywhere, individual targets
2553 # can add further args directly, or indirectly via dependencies
2554 add_project_arguments(cflags, language: ['c'])
2555 add_project_arguments(cppflags, language: ['c'])
2556 add_project_arguments(cflags_warn, language: ['c'])
2557 add_project_arguments(cxxflags, language: ['cpp'])
2558 add_project_arguments(cppflags, language: ['cpp'])
2559 add_project_arguments(cxxflags_warn, language: ['cpp'])
2560 add_project_link_arguments(ldflags, language: ['c', 'cpp'])
2563 # Collect a number of lists of things while recursing through the source
2564 # tree. Later steps then can use those.
2566 # list of targets for various alias targets
2567 backend_targets = []
2568 bin_targets = []
2569 pl_targets = []
2570 contrib_targets = []
2571 testprep_targets = []
2572 nls_targets = []
2575 # Define the tests to distribute them to the correct test styles later
2576 test_deps = []
2577 tests = []
2580 # Default options for targets
2582 # First identify rpaths
2583 bin_install_rpaths = []
2584 lib_install_rpaths = []
2585 mod_install_rpaths = []
2588 # Don't add rpaths on darwin for now - as long as only absolute references to
2589 # libraries are needed, absolute LC_ID_DYLIB ensures libraries can be found in
2590 # their final destination.
2591 if host_system != 'darwin'
2592   # Add absolute path to libdir to rpath. This ensures installed binaries /
2593   # libraries find our libraries (mainly libpq).
2594   bin_install_rpaths += dir_prefix / dir_lib
2595   lib_install_rpaths += dir_prefix / dir_lib
2596   mod_install_rpaths += dir_prefix / dir_lib
2598   # Add extra_lib_dirs to rpath. This ensures we find libraries we depend on.
2599   #
2600   # Not needed on darwin even if we use relative rpaths for our own libraries,
2601   # as the install_name of libraries in extra_lib_dirs will point to their
2602   # location anyway.
2603   bin_install_rpaths += postgres_lib_d
2604   lib_install_rpaths += postgres_lib_d
2605   mod_install_rpaths += postgres_lib_d
2606 endif
2609 # Define arguments for default targets
2611 default_target_args = {
2612   'implicit_include_directories': false,
2613   'install': true,
2616 default_lib_args = default_target_args + {
2617   'name_prefix': '',
2620 internal_lib_args = default_lib_args + {
2621   'build_by_default': false,
2622   'install': false,
2625 default_mod_args = default_lib_args + {
2626   'name_prefix': '',
2627   'install_dir': dir_lib_pkg,
2630 default_bin_args = default_target_args + {
2631   'install_dir': dir_bin,
2634 if get_option('rpath')
2635   default_lib_args += {
2636     'install_rpath': ':'.join(lib_install_rpaths),
2637   }
2639   default_mod_args += {
2640     'install_rpath': ':'.join(mod_install_rpaths),
2641   }
2643   default_bin_args += {
2644     'install_rpath': ':'.join(bin_install_rpaths),
2645   }
2646 endif
2649 # Helper for exporting a limited number of symbols
2650 gen_export_kwargs = {
2651   'input': 'exports.txt',
2652   'output': '@BASENAME@.'+export_file_suffix,
2653   'command': [perl, files('src/tools/gen_export.pl'),
2654    '--format', export_file_format,
2655    '--input', '@INPUT0@', '--output', '@OUTPUT0@'],
2656   'build_by_default': false,
2657   'install': false,
2663 ### Helpers for custom targets used across the tree
2666 catalog_pm = files('src/backend/catalog/Catalog.pm')
2667 perfect_hash_pm = files('src/tools/PerfectHash.pm')
2668 gen_kwlist_deps = [perfect_hash_pm]
2669 gen_kwlist_cmd = [
2670   perl, '-I', '@SOURCE_ROOT@/src/tools',
2671   files('src/tools/gen_keywordlist.pl'),
2672   '--output', '@OUTDIR@', '@INPUT@']
2677 ### windows resources related stuff
2680 if host_system == 'windows'
2681   pg_ico = meson.source_root() / 'src' / 'port' / 'win32.ico'
2682   win32ver_rc = files('src/port/win32ver.rc')
2683   rcgen = find_program('src/tools/rcgen', native: true)
2685   rcgen_base_args = [
2686     '--srcdir', '@SOURCE_DIR@',
2687     '--builddir', meson.build_root(),
2688     '--rcout', '@OUTPUT0@',
2689     '--out', '@OUTPUT1@',
2690     '--input', '@INPUT@',
2691     '@EXTRA_ARGS@',
2692   ]
2694   if cc.get_argument_syntax() == 'msvc'
2695     rc = find_program('rc', required: true)
2696     rcgen_base_args += ['--rc', rc.path()]
2697     rcgen_outputs = ['@BASENAME@.rc', '@BASENAME@.res']
2698   else
2699     windres = find_program('windres', required: true)
2700     rcgen_base_args += ['--windres', windres.path()]
2701     rcgen_outputs = ['@BASENAME@.rc', '@BASENAME@.obj']
2702   endif
2704   # msbuild backend doesn't support this atm
2705   if meson.backend() == 'ninja'
2706     rcgen_base_args += ['--depfile', '@DEPFILE@']
2707   endif
2709   rcgen_bin_args = rcgen_base_args + [
2710     '--VFT_TYPE', 'VFT_APP',
2711     '--FILEENDING', 'exe',
2712     '--ICO', pg_ico
2713   ]
2715   rcgen_lib_args = rcgen_base_args + [
2716     '--VFT_TYPE', 'VFT_DLL',
2717     '--FILEENDING', 'dll',
2718   ]
2720   rc_bin_gen = generator(rcgen,
2721     depfile: '@BASENAME@.d',
2722     arguments: rcgen_bin_args,
2723     output: rcgen_outputs,
2724   )
2726   rc_lib_gen = generator(rcgen,
2727     depfile: '@BASENAME@.d',
2728     arguments: rcgen_lib_args,
2729     output: rcgen_outputs,
2730   )
2731 endif
2735 # headers that the whole build tree depends on
2736 generated_headers = []
2737 # headers that the backend build depends on
2738 generated_backend_headers = []
2739 # configure_files() output, needs a way of converting to file names
2740 configure_files = []
2742 # generated files that might conflict with a partial in-tree autoconf build
2743 generated_sources = []
2744 # same, for paths that differ between autoconf / meson builds
2745 # elements are [dir, [files]]
2746 generated_sources_ac = {}
2749 # First visit src/include - all targets creating headers are defined
2750 # within. That makes it easy to add the necessary dependencies for the
2751 # subsequent build steps.
2753 subdir('src/include')
2755 subdir('config')
2757 # Then through src/port and src/common, as most other things depend on them
2759 frontend_port_code = declare_dependency(
2760   compile_args: ['-DFRONTEND'],
2761   include_directories: [postgres_inc],
2762   dependencies: os_deps,
2765 backend_port_code = declare_dependency(
2766   compile_args: ['-DBUILDING_DLL'],
2767   include_directories: [postgres_inc],
2768   sources: [errcodes], # errcodes.h is needed due to use of ereport
2769   dependencies: os_deps,
2772 subdir('src/port')
2774 frontend_common_code = declare_dependency(
2775   compile_args: ['-DFRONTEND'],
2776   include_directories: [postgres_inc],
2777   sources: generated_headers,
2778   dependencies: [os_deps, zlib, zstd],
2781 backend_common_code = declare_dependency(
2782   compile_args: ['-DBUILDING_DLL'],
2783   include_directories: [postgres_inc],
2784   sources: generated_headers,
2785   dependencies: [os_deps, zlib, zstd],
2788 subdir('src/common')
2790 # all shared libraries should depend on shlib_code
2791 shlib_code = declare_dependency(
2792   link_args: ldflags_sl,
2795 # all static libraries not part of the backend should depend on this
2796 frontend_stlib_code = declare_dependency(
2797   include_directories: [postgres_inc],
2798   link_with: [common_static, pgport_static],
2799   sources: generated_headers,
2800   dependencies: [os_deps, libintl],
2803 # all shared libraries not part of the backend should depend on this
2804 frontend_shlib_code = declare_dependency(
2805   include_directories: [postgres_inc],
2806   link_with: [common_shlib, pgport_shlib],
2807   sources: generated_headers,
2808   dependencies: [shlib_code, os_deps, libintl],
2811 # Dependencies both for static and shared libpq
2812 libpq_deps += [
2813   thread_dep,
2815   gssapi,
2816   ldap_r,
2817   libintl,
2818   ssl,
2821 subdir('src/interfaces/libpq')
2822 # fe_utils depends on libpq
2823 subdir('src/fe_utils')
2825 # for frontend binaries
2826 frontend_code = declare_dependency(
2827   include_directories: [postgres_inc],
2828   link_with: [fe_utils, common_static, pgport_static],
2829   sources: generated_headers,
2830   dependencies: [os_deps, libintl],
2833 backend_both_deps += [
2834   thread_dep,
2835   bsd_auth,
2836   gssapi,
2837   icu,
2838   icu_i18n,
2839   ldap,
2840   libintl,
2841   libxml,
2842   lz4,
2843   pam,
2844   ssl,
2845   systemd,
2846   zlib,
2847   zstd,
2850 backend_mod_deps = backend_both_deps + os_deps
2852 backend_code = declare_dependency(
2853   compile_args: ['-DBUILDING_DLL'],
2854   include_directories: [postgres_inc],
2855   link_args: ldflags_be,
2856   link_with: [],
2857   sources: generated_headers + generated_backend_headers,
2858   dependencies: os_deps + backend_both_deps + backend_deps,
2861 # install these files only during test, not main install
2862 test_install_data = []
2863 test_install_libs = []
2865 # src/backend/meson.build defines backend_mod_code used for extension
2866 # libraries.
2869 # Then through the main sources. That way contrib can have dependencies on
2870 # main sources. Note that this explicitly doesn't enter src/test, right now a
2871 # few regression tests depend on contrib files.
2873 subdir('src')
2875 subdir('contrib')
2877 subdir('src/test')
2878 subdir('src/interfaces/libpq/test')
2879 subdir('src/interfaces/ecpg/test')
2881 subdir('doc/src/sgml')
2883 generated_sources_ac += {'': ['GNUmakefile']}
2885 # After processing src/test, add test_install_libs to the testprep_targets
2886 # to build them
2887 testprep_targets += test_install_libs
2890 # If there are any files in the source directory that we also generate in the
2891 # build directory, they might get preferred over the newly generated files,
2892 # e.g. because of a #include "file", which always will search in the current
2893 # directory first.
2894 message('checking for file conflicts between source and build directory')
2895 conflicting_files = []
2896 potentially_conflicting_files_t = []
2897 potentially_conflicting_files_t += generated_headers
2898 potentially_conflicting_files_t += generated_backend_headers
2899 potentially_conflicting_files_t += generated_backend_sources
2900 potentially_conflicting_files_t += generated_sources
2902 potentially_conflicting_files = []
2904 # convert all sources of potentially conflicting files into uniform shape
2905 foreach t : potentially_conflicting_files_t
2906   potentially_conflicting_files += t.full_path()
2907 endforeach
2908 foreach t : configure_files
2909   t = '@0@'.format(t)
2910   potentially_conflicting_files += meson.current_build_dir() / t
2911 endforeach
2912 foreach sub, fnames : generated_sources_ac
2913   sub = meson.build_root() / sub
2914   foreach fname : fnames
2915     potentially_conflicting_files += sub / fname
2916   endforeach
2917 endforeach
2919 # find and report conflicting files
2920 foreach build_path : potentially_conflicting_files
2921   build_path = host_system == 'windows' ? fs.as_posix(build_path) : build_path
2922   # str.replace is in 0.56
2923   src_path = meson.current_source_dir() / build_path.split(meson.current_build_dir() / '')[1]
2924   if fs.exists(src_path) or fs.is_symlink(src_path)
2925     conflicting_files += src_path
2926   endif
2927 endforeach
2928 # XXX: Perhaps we should generate a file that would clean these up? The list
2929 # can be long.
2930 if conflicting_files.length() > 0
2931   errmsg_cleanup = '''
2932 Conflicting files in source directory:
2933   @0@
2935 The conflicting files need to be removed, either by removing the files listed
2936 above, or by running configure and then make maintainer-clean.
2938   errmsg_cleanup = errmsg_cleanup.format(' '.join(conflicting_files))
2939   error(errmsg_nonclean_base.format(errmsg_cleanup))
2940 endif
2944 ###############################################################
2945 # Install targets
2946 ###############################################################
2949 # We want to define additional install targets beyond what meson provides. For
2950 # that we need to define targets depending on nearly everything. We collected
2951 # the results of i18n.gettext() invocations into nls_targets, that also
2952 # includes maintainer targets though. Collect the ones we want as a dependency.
2954 # i18n.gettext() doesn't return the dependencies before 0.60 - but the gettext
2955 # generation happens during install, so that's not a real issue.
2956 nls_mo_targets = []
2957 if libintl.found() and meson.version().version_compare('>=0.60')
2958   # use range() to avoid the flattening of the list that foreach() would do
2959   foreach off : range(0, nls_targets.length())
2960     # i18n.gettext() list containing 1) list of built .mo files 2) maintainer
2961     # -pot target 3) maintainer -pot target
2962     nls_mo_targets += nls_targets[off][0]
2963   endforeach
2964   alias_target('nls', nls_mo_targets)
2965 endif
2968 all_built = [
2969   backend_targets,
2970   bin_targets,
2971   libpq_st,
2972   pl_targets,
2973   contrib_targets,
2974   nls_mo_targets,
2975   testprep_targets,
2976   ecpg_targets,
2979 # Meson's default install target is quite verbose. Provide one that is quiet.
2980 install_quiet = custom_target('install-quiet',
2981   output: 'install-quiet',
2982   build_always_stale: true,
2983   build_by_default: false,
2984   command: [meson_bin, meson_args, 'install', '--quiet', '--no-rebuild'],
2985   depends: all_built,
2988 # Target to install files used for tests, which aren't installed by default
2989 install_test_files_args = [
2990   install_files,
2991   '--prefix', dir_prefix,
2992   '--install', contrib_data_dir, test_install_data,
2993   '--install', dir_lib_pkg, test_install_libs,
2995 run_target('install-test-files',
2996   command: [python] + install_test_files_args,
2997   depends: testprep_targets,
3002 ###############################################################
3003 # Test prep
3004 ###############################################################
3006 # DESTDIR for the installation we'll run tests in
3007 test_install_destdir = meson.build_root() / 'tmp_install/'
3009 # DESTDIR + prefix appropriately munged
3010 if build_system != 'windows'
3011   # On unixoid systems this is trivial, we just prepend the destdir
3012   assert(dir_prefix.startswith('/')) # enforced by meson
3013   test_install_location = '@0@@1@'.format(test_install_destdir, dir_prefix)
3014 else
3015   # drives, drive-relative paths, etc make this complicated on windows, call
3016   # into a copy of meson's logic for it
3017   command = [
3018     python, '-c',
3019     'import sys; from pathlib import PurePath; d1=sys.argv[1]; d2=sys.argv[2]; print(str(PurePath(d1, *PurePath(d2).parts[1:])))',
3020     test_install_destdir, dir_prefix]
3021   test_install_location = run_command(command, check: true).stdout().strip()
3022 endif
3024 meson_install_args = meson_args + ['install'] + {
3025     'meson': ['--quiet', '--only-changed', '--no-rebuild'],
3026     'muon': []
3027 }[meson_impl]
3029 # setup tests should be run first,
3030 # so define priority for these
3031 setup_tests_priority = 100
3032 test('tmp_install',
3033     meson_bin, args: meson_install_args ,
3034     env: {'DESTDIR':test_install_destdir},
3035     priority: setup_tests_priority,
3036     timeout: 300,
3037     is_parallel: false,
3038     suite: ['setup'])
3040 test('install_test_files',
3041     python,
3042     args: install_test_files_args + ['--destdir', test_install_destdir],
3043     priority: setup_tests_priority,
3044     is_parallel: false,
3045     suite: ['setup'])
3047 test_result_dir = meson.build_root() / 'testrun'
3050 # XXX: pg_regress doesn't assign unique ports on windows. To avoid the
3051 # inevitable conflicts from running tests in parallel, hackishly assign
3052 # different ports for different tests.
3054 testport = 40000
3056 test_env = environment()
3058 temp_install_bindir = test_install_location / get_option('bindir')
3059 test_env.set('PG_REGRESS', pg_regress.full_path())
3060 test_env.set('REGRESS_SHLIB', regress_module.full_path())
3062 # Test suites that are not safe by default but can be run if selected
3063 # by the user via the whitespace-separated list in variable PG_TEST_EXTRA.
3064 # Export PG_TEST_EXTRA so it can be checked in individual tap tests.
3065 test_env.set('PG_TEST_EXTRA', get_option('PG_TEST_EXTRA'))
3067 # Add the temporary installation to the library search path on platforms where
3068 # that works (everything but windows, basically). On windows everything
3069 # library-like gets installed into bindir, solving that issue.
3070 if library_path_var != ''
3071   test_env.prepend(library_path_var, test_install_location / get_option('libdir'))
3072 endif
3076 ###############################################################
3077 # Test Generation
3078 ###############################################################
3080 # When using a meson version understanding exclude_suites, define a
3081 # 'tmp_install' test setup (the default) that excludes tests running against a
3082 # pre-existing install and a 'running' setup that conflicts with creation of
3083 # the temporary installation and tap tests (which don't support running
3084 # against a running server).
3086 running_suites = []
3087 install_suites = []
3088 if meson.version().version_compare('>=0.57')
3089   runningcheck = true
3090 else
3091   runningcheck = false
3092 endif
3094 testwrap = files('src/tools/testwrap')
3096 foreach test_dir : tests
3097   testwrap_base = [
3098     testwrap,
3099     '--basedir', meson.build_root(),
3100     '--srcdir', test_dir['sd'],
3101   ]
3103   foreach kind, v : test_dir
3104     if kind in ['sd', 'bd', 'name']
3105       continue
3106     endif
3108     t = test_dir[kind]
3110     if kind in ['regress', 'isolation', 'ecpg']
3111       if kind == 'regress'
3112         runner = pg_regress
3113         fallback_dbname = 'regression_@0@'
3114       elif kind == 'isolation'
3115         runner = pg_isolation_regress
3116         fallback_dbname = 'isolation_regression_@0@'
3117       elif kind == 'ecpg'
3118         runner = pg_regress_ecpg
3119         fallback_dbname = 'ecpg_regression_@0@'
3120       endif
3122       test_group = test_dir['name']
3123       test_group_running = test_dir['name'] + '-running'
3125       test_output = test_result_dir / test_group / kind
3126       test_output_running = test_result_dir / test_group_running/ kind
3128       # Unless specified by the test, choose a non-conflicting database name,
3129       # to avoid conflicts when running against existing server.
3130       dbname = t.get('dbname',
3131         fallback_dbname.format(test_dir['name']))
3133       test_command_base = [
3134         runner.full_path(),
3135         '--inputdir', t.get('inputdir', test_dir['sd']),
3136         '--expecteddir', t.get('expecteddir', test_dir['sd']),
3137         '--bindir', '',
3138         '--dlpath', test_dir['bd'],
3139         '--max-concurrent-tests=20',
3140         '--dbname', dbname,
3141       ] + t.get('regress_args', [])
3143       test_selection = []
3144       if t.has_key('schedule')
3145         test_selection += ['--schedule', t['schedule'],]
3146       endif
3148       if kind == 'isolation'
3149         test_selection += t.get('specs', [])
3150       else
3151         test_selection += t.get('sql', [])
3152       endif
3154       env = test_env
3155       env.prepend('PATH', temp_install_bindir, test_dir['bd'])
3157       test_kwargs = {
3158         'protocol': 'tap',
3159         'priority': 10,
3160         'timeout': 1000,
3161         'depends': test_deps + t.get('deps', []),
3162         'env': env,
3163       } + t.get('test_kwargs', {})
3165       test(test_group / kind,
3166         python,
3167         args: [
3168           testwrap_base,
3169           '--testgroup', test_group,
3170           '--testname', kind,
3171           '--',
3172           test_command_base,
3173           '--outputdir', test_output,
3174           '--temp-instance', test_output / 'tmp_check',
3175           '--port', testport.to_string(),
3176           test_selection,
3177         ],
3178         suite: test_group,
3179         kwargs: test_kwargs,
3180       )
3181       install_suites += test_group
3183       # some tests can't support running against running DB
3184       if runningcheck and t.get('runningcheck', true)
3185         test(test_group_running / kind,
3186           python,
3187           args: [
3188             testwrap_base,
3189             '--testgroup', test_group_running,
3190             '--testname', kind,
3191             '--',
3192             test_command_base,
3193             '--outputdir', test_output_running,
3194             test_selection,
3195           ],
3196           is_parallel: t.get('runningcheck-parallel', true),
3197           suite: test_group_running,
3198           kwargs: test_kwargs,
3199         )
3200         running_suites += test_group_running
3201       endif
3203       testport += 1
3204     elif kind == 'tap'
3205       if not tap_tests_enabled
3206         continue
3207       endif
3209       test_command = [
3210         perl.path(),
3211         '-I', meson.source_root() / 'src/test/perl',
3212         '-I', test_dir['sd'],
3213       ]
3215       # Add temporary install, the build directory for non-installed binaries and
3216       # also test/ for non-installed test binaries built separately.
3217       env = test_env
3218       env.prepend('PATH', temp_install_bindir, test_dir['bd'], test_dir['bd'] / 'test')
3220       foreach name, value : t.get('env', {})
3221         env.set(name, value)
3222       endforeach
3224       test_group = test_dir['name']
3225       test_kwargs = {
3226         'protocol': 'tap',
3227         'suite': test_group,
3228         'timeout': 1000,
3229         'depends': test_deps + t.get('deps', []),
3230         'env': env,
3231       } + t.get('test_kwargs', {})
3233       foreach onetap : t['tests']
3234         # Make tap test names prettier, remove t/ and .pl
3235         onetap_p = onetap
3236         if onetap_p.startswith('t/')
3237           onetap_p = onetap.split('t/')[1]
3238         endif
3239         if onetap_p.endswith('.pl')
3240           onetap_p = fs.stem(onetap_p)
3241         endif
3243         test(test_dir['name'] / onetap_p,
3244           python,
3245           kwargs: test_kwargs,
3246           args: testwrap_base + [
3247             '--testgroup', test_dir['name'],
3248             '--testname', onetap_p,
3249             '--', test_command,
3250             test_dir['sd'] / onetap,
3251           ],
3252         )
3253       endforeach
3254       install_suites += test_group
3255     else
3256       error('unknown kind @0@ of test in @1@'.format(kind, test_dir['sd']))
3257     endif
3259   endforeach # kinds of tests
3261 endforeach # directories with tests
3263 # repeat condition so meson realizes version dependency
3264 if meson.version().version_compare('>=0.57')
3265   add_test_setup('tmp_install',
3266     is_default: true,
3267     exclude_suites: running_suites)
3268   add_test_setup('running',
3269     exclude_suites: ['setup'] + install_suites)
3270 endif
3274 ###############################################################
3275 # Pseudo targets
3276 ###############################################################
3278 alias_target('backend', backend_targets)
3279 alias_target('bin', bin_targets + [libpq_st])
3280 alias_target('pl', pl_targets)
3281 alias_target('contrib', contrib_targets)
3282 alias_target('testprep', testprep_targets)
3283 alias_target('install-world', install_quiet, installdocs)
3287 ###############################################################
3288 # The End, The End, My Friend
3289 ###############################################################
3291 if meson.version().version_compare('>=0.57')
3293   summary(
3294     {
3295       'data block size': '@0@ kB'.format(cdata.get('BLCKSZ') / 1024),
3296       'WAL block size': '@0@ kB'.format(cdata.get('XLOG_BLCKSZ') / 1024),
3297       'segment size': get_option('segsize_blocks') != 0 ?
3298         '@0@ blocks'.format(cdata.get('RELSEG_SIZE')) :
3299         '@0@ GB'.format(get_option('segsize')),
3300     },
3301     section: 'Data layout',
3302   )
3304   summary(
3305     {
3306       'host system': '@0@ @1@'.format(host_system, host_cpu),
3307       'build system': '@0@ @1@'.format(build_machine.system(),
3308                                        build_machine.cpu_family()),
3309     },
3310     section: 'System',
3311   )
3313   summary(
3314     {
3315       'linker': '@0@'.format(cc.get_linker_id()),
3316       'C compiler': '@0@ @1@'.format(cc.get_id(), cc.version()),
3317     },
3318     section: 'Compiler',
3319   )
3321   summary(
3322     {
3323       'CPP FLAGS': ' '.join(cppflags),
3324       'C FLAGS, functional': ' '.join(cflags),
3325       'C FLAGS, warnings': ' '.join(cflags_warn),
3326       'C FLAGS, modules': ' '.join(cflags_mod),
3327       'C FLAGS, user specified': ' '.join(get_option('c_args')),
3328       'LD FLAGS': ' '.join(ldflags + get_option('c_link_args')),
3329     },
3330     section: 'Compiler Flags',
3331   )
3333   if llvm.found()
3334     summary(
3335       {
3336         'C++ compiler': '@0@ @1@'.format(cpp.get_id(), cpp.version()),
3337       },
3338       section: 'Compiler',
3339     )
3341     summary(
3342       {
3343         'C++ FLAGS, functional': ' '.join(cxxflags),
3344         'C++ FLAGS, warnings': ' '.join(cxxflags_warn),
3345         'C++ FLAGS, user specified': ' '.join(get_option('cpp_args')),
3346       },
3347       section: 'Compiler Flags',
3348     )
3349   endif
3351   summary(
3352     {
3353       'bison': '@0@ @1@'.format(bison.full_path(), bison_version),
3354       'dtrace': dtrace,
3355       'flex': '@0@ @1@'.format(flex.full_path(), flex_version),
3356     },
3357     section: 'Programs',
3358   )
3360   summary(
3361     {
3362       'bonjour': bonjour,
3363       'bsd_auth': bsd_auth,
3364       'docs': docs_dep,
3365       'docs_pdf': docs_pdf_dep,
3366       'gss': gssapi,
3367       'icu': icu,
3368       'ldap': ldap,
3369       'libxml': libxml,
3370       'libxslt': libxslt,
3371       'llvm': llvm,
3372       'lz4': lz4,
3373       'nls': libintl,
3374       'openssl': ssl,
3375       'pam': pam,
3376       'plperl': perl_dep,
3377       'plpython': python3_dep,
3378       'pltcl': tcl_dep,
3379       'readline': readline,
3380       'selinux': selinux,
3381       'systemd': systemd,
3382       'uuid': uuid,
3383       'zlib': zlib,
3384       'zstd': zstd,
3385     },
3386     section: 'External libraries',
3387   )
3389 endif