Fix reported runtime for single tests in pg_regress
[pgsql.git] / meson.build
blob096044628c5dacf3c8263611fb8e2dd41ee89eea
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: '16devel',
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 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 flex_wrapper = files('src/tools/pgflex')
365 flex_cmd = [python, flex_wrapper,
366   '--builddir', '@BUILD_ROOT@',
367   '--srcdir', '@SOURCE_ROOT@',
368   '--privatedir', '@PRIVATE_DIR@',
369   '--flex', flex, '--perl', perl,
370   '-i', '@INPUT@', '-o', '@OUTPUT0@',
373 wget = find_program('wget', required: false, native: true)
374 wget_flags = ['-O', '@OUTPUT0@', '--no-use-server-timestamps']
376 install_files = files('src/tools/install_files')
380 ###############################################################
381 # Path to meson (for tests etc)
382 ###############################################################
384 # NB: this should really be part of meson, see
385 # https://github.com/mesonbuild/meson/issues/8511
386 meson_binpath_r = run_command(python, 'src/tools/find_meson', check: true)
388 if meson_binpath_r.returncode() != 0 or meson_binpath_r.stdout() == ''
389   error('huh, could not run find_meson.\nerrcode: @0@\nstdout: @1@\nstderr: @2@'.format(
390     meson_binpath_r.returncode(),
391     meson_binpath_r.stdout(),
392     meson_binpath_r.stderr()))
393 endif
395 meson_binpath_s = meson_binpath_r.stdout().split('\n')
396 meson_binpath_len = meson_binpath_s.length()
398 if meson_binpath_len < 1
399   error('unexpected introspect line @0@'.format(meson_binpath_r.stdout()))
400 endif
402 i = 0
403 meson_impl = ''
404 meson_binpath = ''
405 meson_args = []
406 foreach e : meson_binpath_s
407   if i == 0
408     meson_impl = e
409   elif i == 1
410     meson_binpath = e
411   else
412     meson_args += e
413   endif
414   i += 1
415 endforeach
417 if meson_impl not in ['muon', 'meson']
418   error('unknown meson implementation "@0@"'.format(meson_impl))
419 endif
421 meson_bin = find_program(meson_binpath, native: true)
425 ###############################################################
426 # Option Handling
427 ###############################################################
429 cdata.set('USE_ASSERT_CHECKING', get_option('cassert') ? 1 : false)
431 blocksize = get_option('blocksize').to_int() * 1024
433 if get_option('segsize_blocks') != 0
434   if get_option('segsize') != 1
435     warning('both segsize and segsize_blocks specified, segsize_blocks wins')
436   endif
438   segsize = get_option('segsize_blocks')
439 else
440   segsize = (get_option('segsize') * 1024 * 1024 * 1024) / blocksize
441 endif
443 cdata.set('BLCKSZ', blocksize, description:
444 '''Size of a disk block --- this also limits the size of a tuple. You can set
445    it bigger if you need bigger tuples (although TOAST should reduce the need
446    to have large tuples, since fields can be spread across multiple tuples).
447    BLCKSZ must be a power of 2. The maximum possible value of BLCKSZ is
448    currently 2^15 (32768). This is determined by the 15-bit widths of the
449    lp_off and lp_len fields in ItemIdData (see include/storage/itemid.h).
450    Changing BLCKSZ requires an initdb.''')
452 cdata.set('XLOG_BLCKSZ', get_option('wal_blocksize').to_int() * 1024)
453 cdata.set('RELSEG_SIZE', segsize)
454 cdata.set('DEF_PGPORT', get_option('pgport'))
455 cdata.set_quoted('DEF_PGPORT_STR', get_option('pgport').to_string())
456 cdata.set_quoted('PG_KRB_SRVNAM', get_option('krb_srvnam'))
457 if get_option('system_tzdata') != ''
458   cdata.set_quoted('SYSTEMTZDIR', get_option('system_tzdata'))
459 endif
463 ###############################################################
464 # Directories
465 ###############################################################
467 # These are set by the equivalent --xxxdir configure options.  We
468 # append "postgresql" to some of them, if the string does not already
469 # contain "pgsql" or "postgres", in order to avoid directory clutter.
471 pkg = 'postgresql'
473 dir_prefix = get_option('prefix')
475 dir_prefix_contains_pg = (dir_prefix.contains('pgsql') or dir_prefix.contains('postgres'))
477 dir_bin = get_option('bindir')
479 dir_data = get_option('datadir')
480 if not (dir_prefix_contains_pg or dir_data.contains('pgsql') or dir_data.contains('postgres'))
481   dir_data = dir_data / pkg
482 endif
484 dir_sysconf = get_option('sysconfdir')
485 if not (dir_prefix_contains_pg or dir_sysconf.contains('pgsql') or dir_sysconf.contains('postgres'))
486   dir_sysconf = dir_sysconf / pkg
487 endif
489 dir_lib = get_option('libdir')
491 dir_lib_pkg = dir_lib
492 if not (dir_prefix_contains_pg or dir_lib_pkg.contains('pgsql') or dir_lib_pkg.contains('postgres'))
493   dir_lib_pkg = dir_lib_pkg / pkg
494 endif
496 dir_pgxs = dir_lib_pkg / 'pgxs'
498 dir_include = get_option('includedir')
500 dir_include_pkg = dir_include
501 dir_include_pkg_rel = ''
502 if not (dir_prefix_contains_pg or dir_include_pkg.contains('pgsql') or dir_include_pkg.contains('postgres'))
503   dir_include_pkg = dir_include_pkg / pkg
504   dir_include_pkg_rel = pkg
505 endif
507 dir_man = get_option('mandir')
509 # FIXME: These used to be separately configurable - worth adding?
510 dir_doc = get_option('datadir') / 'doc' / 'postgresql'
511 dir_doc_html = dir_doc / 'html'
513 dir_locale = get_option('localedir')
516 # Derived values
517 dir_bitcode = dir_lib_pkg / 'bitcode'
518 dir_include_internal = dir_include_pkg / 'internal'
519 dir_include_server = dir_include_pkg / 'server'
520 dir_include_extension = dir_include_server / 'extension'
521 dir_data_extension = dir_data / 'extension'
525 ###############################################################
526 # Search paths, preparation for compiler tests
528 # NB: Arguments added later are not automatically used for subsequent
529 # configuration-time checks (so they are more isolated). If they should be
530 # used, they need to be added to test_c_args as well.
531 ###############################################################
533 postgres_inc = [include_directories(postgres_inc_d)]
534 test_lib_d = postgres_lib_d
535 test_c_args = cppflags + cflags
539 ###############################################################
540 # Library: bsd-auth
541 ###############################################################
543 bsd_authopt = get_option('bsd_auth')
544 bsd_auth = not_found_dep
545 if cc.check_header('bsd_auth.h', required: bsd_authopt,
546     args: test_c_args, include_directories: postgres_inc)
547   cdata.set('USE_BSD_AUTH', 1)
548   bsd_auth = declare_dependency()
549 endif
553 ###############################################################
554 # Library: bonjour
556 # For now don't search for DNSServiceRegister in a library - only Apple's
557 # Bonjour implementation, which is always linked, works.
558 ###############################################################
560 bonjouropt = get_option('bonjour')
561 bonjour = dependency('', required : false)
562 if cc.check_header('dns_sd.h', required: bonjouropt,
563     args: test_c_args, include_directories: postgres_inc) and \
564    cc.has_function('DNSServiceRegister',
565     args: test_c_args, include_directories: postgres_inc)
566   cdata.set('USE_BONJOUR', 1)
567   bonjour = declare_dependency()
568 endif
572 ###############################################################
573 # Option: docs in HTML and man page format
574 ###############################################################
576 docs_opt = get_option('docs')
577 docs_dep = not_found_dep
578 if not docs_opt.disabled()
579   if xmllint_bin.found() and xsltproc_bin.found()
580     docs_dep = declare_dependency()
581   elif docs_opt.enabled()
582     error('missing required tools for docs in HTML / man page format')
583   endif
584 endif
588 ###############################################################
589 # Option: docs in PDF format
590 ###############################################################
592 docs_pdf_opt = get_option('docs_pdf')
593 docs_pdf_dep = not_found_dep
594 if not docs_pdf_opt.disabled()
595   fop = find_program(get_option('FOP'), native: true, required: docs_pdf_opt)
596   if xmllint_bin.found() and xsltproc_bin.found() and fop.found()
597     docs_pdf_dep = declare_dependency()
598   elif docs_pdf_opt.enabled()
599     error('missing required tools for docs in PDF format')
600   endif
601 endif
605 ###############################################################
606 # Library: GSSAPI
607 ###############################################################
609 gssapiopt = get_option('gssapi')
610 krb_srvtab = ''
611 have_gssapi = false
612 if not gssapiopt.disabled()
613   gssapi = dependency('krb5-gssapi', required: gssapiopt)
614   have_gssapi = gssapi.found()
616   if not have_gssapi
617   elif cc.check_header('gssapi/gssapi.h', dependencies: gssapi, required: false,
618       args: test_c_args, include_directories: postgres_inc)
619     cdata.set('HAVE_GSSAPI_GSSAPI_H', 1)
620   elif cc.check_header('gssapi.h', args: test_c_args, dependencies: gssapi, required: gssapiopt)
621     cdata.set('HAVE_GSSAPI_H', 1)
622   else
623     have_gssapi = false
624   endif
626   if not have_gssapi
627   elif cc.check_header('gssapi/gssapi_ext.h', dependencies: gssapi, required: false,
628       args: test_c_args, include_directories: postgres_inc)
629     cdata.set('HAVE_GSSAPI_GSSAPI_EXT_H', 1)
630   elif cc.check_header('gssapi_ext.h', args: test_c_args, dependencies: gssapi, required: gssapiopt)
631     cdata.set('HAVE_GSSAPI_EXT_H', 1)
632   else
633     have_gssapi = false
634   endif
636   if not have_gssapi
637   elif cc.has_function('gss_store_cred_into', dependencies: gssapi,
638       args: test_c_args, include_directories: postgres_inc)
639     cdata.set('ENABLE_GSS', 1)
641     krb_srvtab = 'FILE:/@0@/krb5.keytab)'.format(get_option('sysconfdir'))
642     cdata.set_quoted('PG_KRB_SRVTAB', krb_srvtab)
643   elif gssapiopt.enabled()
644     error('''could not find function 'gss_store_cred_into' required for GSSAPI''')
645   else
646     have_gssapi = false
647   endif
648 endif
649 if not have_gssapi
650   gssapi = not_found_dep
651 endif
655 ###############################################################
656 # Library: ldap
657 ###############################################################
659 ldapopt = get_option('ldap')
660 if ldapopt.disabled()
661   ldap = not_found_dep
662   ldap_r = not_found_dep
663 elif host_system == 'windows'
664   ldap = cc.find_library('wldap32', required: ldapopt)
665   ldap_r = ldap
666 else
667   # macos framework dependency is buggy for ldap (one can argue whether it's
668   # Apple's or meson's fault), leading to an endless recursion with ldap.h
669   # including itself. See https://github.com/mesonbuild/meson/issues/10002
670   # Luckily we only need pkg-config support, so the workaround isn't
671   # complicated.
672   ldap = dependency('ldap', method: 'pkg-config', required: false)
673   ldap_r = ldap
675   # Before 2.5 openldap didn't have a pkg-config file, and it might not be
676   # installed
677   if not ldap.found()
678     ldap = cc.find_library('ldap', required: ldapopt, dirs: test_lib_d,
679       has_headers: 'ldap.h', header_include_directories: postgres_inc)
681     # The separate ldap_r library only exists in OpenLDAP < 2.5, and if we
682     # have 2.5 or later, we shouldn't even probe for ldap_r (we might find a
683     # library from a separate OpenLDAP installation).  The most reliable
684     # way to check that is to check for a function introduced in 2.5.
685     if not ldap.found()
686       # don't have ldap, we shouldn't check for ldap_r
687     elif cc.has_function('ldap_verify_credentials',
688         dependencies: ldap, args: test_c_args)
689       ldap_r = ldap # ldap >= 2.5, no need for ldap_r
690     else
692       # Use ldap_r for FE if available, else assume ldap is thread-safe.
693       ldap_r = cc.find_library('ldap_r', required: false, dirs: test_lib_d,
694         has_headers: 'ldap.h', header_include_directories: postgres_inc)
695       if not ldap_r.found()
696         ldap_r = ldap
697       else
698         # On some platforms ldap_r fails to link without PTHREAD_LIBS.
699         ldap_r = declare_dependency(dependencies: [ldap_r, thread_dep])
700       endif
702       # PostgreSQL sometimes loads libldap_r and plain libldap into the same
703       # process.  Check for OpenLDAP versions known not to tolerate doing so;
704       # assume non-OpenLDAP implementations are safe.  The dblink test suite
705       # exercises the hazardous interaction directly.
706       compat_test_code = '''
707 #include <ldap.h>
708 #if !defined(LDAP_VENDOR_VERSION) || \
709      (defined(LDAP_API_FEATURE_X_OPENLDAP) && \
710       LDAP_VENDOR_VERSION >= 20424 && LDAP_VENDOR_VERSION <= 20431)
711 choke me
712 #endif
714       if not cc.compiles(compat_test_code,
715           name: 'LDAP implementation compatible',
716           dependencies: ldap, args: test_c_args)
717         warning('''
718 *** With OpenLDAP versions 2.4.24 through 2.4.31, inclusive, each backend
719 *** process that loads libpq (via WAL receiver, dblink, or postgres_fdw) and
720 *** also uses LDAP will crash on exit.''')
721       endif
722     endif
723   endif
725   # XXX: this shouldn't be tested in the windows case, but should be tested in
726   # the dependency() success case
727   if ldap.found() and cc.has_function('ldap_initialize',
728       dependencies: ldap, args: test_c_args)
729     cdata.set('HAVE_LDAP_INITIALIZE', 1)
730   endif
731 endif
733 if ldap.found()
734   assert(ldap_r.found())
735   cdata.set('USE_LDAP', 1)
736 else
737   assert(not ldap_r.found())
738 endif
742 ###############################################################
743 # Library: LLVM
744 ###############################################################
746 llvmopt = get_option('llvm')
747 if not llvmopt.disabled()
748   add_languages('cpp', required: true, native: false)
749   llvm = dependency('llvm', version: '>=3.9', method: 'config-tool', required: llvmopt)
751   if llvm.found()
753     cdata.set('USE_LLVM', 1)
755     cpp = meson.get_compiler('cpp')
757     llvm_binpath = llvm.get_variable(configtool: 'bindir')
759     ccache = find_program('ccache', native: true, required: false)
760     clang = find_program(llvm_binpath / 'clang', required: true)
761   endif
762 else
763   llvm = not_found_dep
764 endif
768 ###############################################################
769 # Library: icu
770 ###############################################################
772 icuopt = get_option('icu')
773 if not icuopt.disabled()
774   icu = dependency('icu-uc', required: icuopt.enabled())
775   icu_i18n = dependency('icu-i18n', required: icuopt.enabled())
777   if icu.found()
778     cdata.set('USE_ICU', 1)
779   endif
781 else
782   icu = not_found_dep
783   icu_i18n = not_found_dep
784 endif
788 ###############################################################
789 # Library: libxml
790 ###############################################################
792 libxmlopt = get_option('libxml')
793 if not libxmlopt.disabled()
794   libxml = dependency('libxml-2.0', required: libxmlopt, version: '>= 2.6.23')
796   if libxml.found()
797     cdata.set('USE_LIBXML', 1)
798   endif
799 else
800   libxml = not_found_dep
801 endif
805 ###############################################################
806 # Library: libxslt
807 ###############################################################
809 libxsltopt = get_option('libxslt')
810 if not libxsltopt.disabled()
811   libxslt = dependency('libxslt', required: libxsltopt)
813   if libxslt.found()
814     cdata.set('USE_LIBXSLT', 1)
815   endif
816 else
817   libxslt = not_found_dep
818 endif
822 ###############################################################
823 # Library: lz4
824 ###############################################################
826 lz4opt = get_option('lz4')
827 if not lz4opt.disabled()
828   lz4 = dependency('liblz4', required: lz4opt)
830   if lz4.found()
831     cdata.set('USE_LZ4', 1)
832     cdata.set('HAVE_LIBLZ4', 1)
833   endif
835 else
836   lz4 = not_found_dep
837 endif
841 ###############################################################
842 # Library: Tcl (for pltcl)
844 # NB: tclConfig.sh is used in autoconf build for getting
845 # TCL_SHARED_BUILD, TCL_INCLUDE_SPEC, TCL_LIBS and TCL_LIB_SPEC
846 # variables. For now we have not seen a need to copy
847 # that behaviour to the meson build.
848 ###############################################################
850 tclopt = get_option('pltcl')
851 tcl_version = get_option('tcl_version')
852 tcl_dep = not_found_dep
853 if not tclopt.disabled()
855   # via pkg-config
856   tcl_dep = dependency(tcl_version, required: false)
858   if not tcl_dep.found()
859     tcl_dep = cc.find_library(tcl_version,
860       required: tclopt,
861       dirs: test_lib_d)
862   endif
864   if not cc.has_header('tcl.h', dependencies: tcl_dep, required: tclopt)
865     tcl_dep = not_found_dep
866   endif
867 endif
871 ###############################################################
872 # Library: pam
873 ###############################################################
875 pamopt = get_option('pam')
876 if not pamopt.disabled()
877   pam = dependency('pam', required: false)
879   if not pam.found()
880     pam = cc.find_library('pam', required: pamopt, dirs: test_lib_d)
881   endif
883   if pam.found()
884     pam_header_found = false
886     # header file <security/pam_appl.h> or <pam/pam_appl.h> is required for PAM.
887     if cc.check_header('security/pam_appl.h', dependencies: pam, required: false,
888         args: test_c_args, include_directories: postgres_inc)
889       cdata.set('HAVE_SECURITY_PAM_APPL_H', 1)
890       pam_header_found = true
891     elif cc.check_header('pam/pam_appl.h', dependencies: pam, required: pamopt,
892         args: test_c_args, include_directories: postgres_inc)
893       cdata.set('HAVE_PAM_PAM_APPL_H', 1)
894       pam_header_found = true
895     endif
897     if pam_header_found
898       cdata.set('USE_PAM', 1)
899     else
900       pam = not_found_dep
901     endif
902   endif
903 else
904   pam = not_found_dep
905 endif
909 ###############################################################
910 # Library: Perl (for plperl)
911 ###############################################################
913 perlopt = get_option('plperl')
914 perl_dep = not_found_dep
915 if not perlopt.disabled()
916   perl_may_work = true
918   # First verify that perl has the necessary dependencies installed
919   perl_mods = run_command(
920     [perl,
921      '-MConfig', '-MOpcode', '-MExtUtils::Embed', '-MExtUtils::ParseXS',
922      '-e', ''],
923     check: false)
924   if perl_mods.returncode() != 0
925     perl_may_work = false
926     perl_msg = 'perl installation does not have the required modules'
927   endif
929   # Then inquire perl about its configuration
930   if perl_may_work
931     perl_conf_cmd = [perl, '-MConfig', '-e', 'print $Config{$ARGV[0]}']
932     perlversion = run_command(perl_conf_cmd, 'api_versionstring', check: true).stdout()
933     archlibexp = run_command(perl_conf_cmd, 'archlibexp', check: true).stdout()
934     privlibexp = run_command(perl_conf_cmd, 'privlibexp', check: true).stdout()
935     useshrplib = run_command(perl_conf_cmd, 'useshrplib', check: true).stdout()
937     perl_inc_dir = '@0@/CORE'.format(archlibexp)
939     if perlversion.version_compare('< 5.14')
940       perl_may_work = false
941       perl_msg = 'Perl version 5.14 or later is required, but this is @0@'.format(perlversion)
942     elif useshrplib != 'true'
943       perl_may_work = false
944       perl_msg = 'need a shared perl'
945     endif
946   endif
948   if perl_may_work
949     # On most platforms, archlibexp is also where the Perl include files live ...
950     perl_ccflags = ['-I@0@'.format(perl_inc_dir)]
951     # ... but on newer macOS versions, we must use -iwithsysroot to look
952     # under sysroot
953     if not fs.is_file('@0@/perl.h'.format(perl_inc_dir)) and \
954        fs.is_file('@0@@1@/perl.h'.format(pg_sysroot, perl_inc_dir))
955       perl_ccflags = ['-iwithsysroot', perl_inc_dir]
956     endif
958     # check compiler finds header
959     if not cc.has_header('perl.h', required: false,
960         args: test_c_args + perl_ccflags, include_directories: postgres_inc)
961       perl_may_work = false
962       perl_msg = 'missing perl.h'
963     endif
964   endif
966   if perl_may_work
967     perl_ccflags_r = run_command(perl_conf_cmd, 'ccflags', check: true).stdout()
969     # See comments for PGAC_CHECK_PERL_EMBED_CCFLAGS in perl.m4
970     foreach flag : perl_ccflags_r.split(' ')
971       if flag.startswith('-D') and \
972           (not flag.startswith('-D_') or flag == '_USE_32BIT_TIME_T')
973         perl_ccflags += flag
974       endif
975     endforeach
977     if host_system == 'windows'
978       perl_ccflags += ['-DPLPERL_HAVE_UID_GID']
980       if cc.get_id() == 'msvc'
981         # prevent binary mismatch between MSVC built plperl and Strawberry or
982         # msys ucrt perl libraries
983         perl_ccflags += ['-DNO_THREAD_SAFE_LOCALE']
984       endif
985     endif
987     message('CCFLAGS recommended by perl: @0@'.format(perl_ccflags_r))
988     message('CCFLAGS for embedding perl: @0@'.format(' '.join(perl_ccflags)))
990     # We are after Embed's ldopts, but without the subset mentioned in
991     # Config's ccdlflags and ldflags.  (Those are the choices of those who
992     # built the Perl installation, which are not necessarily appropriate
993     # for building PostgreSQL.)
994     ldopts = run_command(perl, '-MExtUtils::Embed', '-e', 'ldopts', check: true).stdout().strip()
995     undesired = run_command(perl_conf_cmd, 'ccdlflags', check: true).stdout().split()
996     undesired += run_command(perl_conf_cmd, 'ldflags', check: true).stdout().split()
998     perl_ldopts = []
999     foreach ldopt : ldopts.split(' ')
1000       if ldopt == '' or ldopt in undesired
1001         continue
1002       endif
1004       perl_ldopts += ldopt.strip('"')
1005     endforeach
1007     message('LDFLAGS recommended by perl: "@0@"'.format(ldopts))
1008     message('LDFLAGS for embedding perl: "@0@"'.format(' '.join(perl_ldopts)))
1010     perl_dep_int = declare_dependency(
1011       compile_args: perl_ccflags,
1012       link_args: perl_ldopts,
1013       version: perlversion,
1014     )
1016     # While we're at it, check that we can link to libperl.
1017     # On most platforms, if perl.h is there then libperl.so will be too, but
1018     # at this writing Debian packages them separately.
1019     perl_link_test = '''
1020 /* see plperl.h */
1021 #ifdef _MSC_VER
1022 #define __inline__ inline
1023 #endif
1024 #include <EXTERN.h>
1025 #include <perl.h>
1026 int main(void)
1028 perl_alloc();
1029 }'''
1030     if not cc.links(perl_link_test, name: 'libperl',
1031           args: test_c_args + perl_ccflags + perl_ldopts,
1032           include_directories: postgres_inc)
1033       perl_may_work = false
1034       perl_msg = 'missing libperl'
1035     endif
1037   endif # perl_may_work
1039   if perl_may_work
1040     perl_dep = perl_dep_int
1041   else
1042     if perlopt.enabled()
1043       error('dependency plperl failed: @0@'.format(perl_msg))
1044     else
1045       message('disabling optional dependency plperl: @0@'.format(perl_msg))
1046     endif
1047   endif
1048 endif
1052 ###############################################################
1053 # Library: Python (for plpython)
1054 ###############################################################
1056 pyopt = get_option('plpython')
1057 if not pyopt.disabled()
1058   pm = import('python')
1059   python3_inst = pm.find_installation(required: pyopt.enabled())
1060   python3_dep = python3_inst.dependency(embed: true, required: pyopt.enabled())
1061   if not cc.check_header('Python.h', dependencies: python3_dep, required: pyopt.enabled())
1062     python3_dep = not_found_dep
1063   endif
1064 else
1065   python3_dep = not_found_dep
1066 endif
1070 ###############################################################
1071 # Library: Readline
1072 ###############################################################
1074 if not get_option('readline').disabled()
1075   libedit_preferred = get_option('libedit_preferred')
1076   # Set the order of readline dependencies
1077   check_readline_deps = libedit_preferred ? \
1078     ['libedit', 'readline'] : ['readline', 'libedit']
1080   foreach readline_dep : check_readline_deps
1081     readline = dependency(readline_dep, required: false)
1082     if not readline.found()
1083       readline = cc.find_library(readline_dep,
1084         required: get_option('readline').enabled(),
1085         dirs: test_lib_d)
1086     endif
1087     if readline.found()
1088       break
1089     endif
1090   endforeach
1092   if readline.found()
1093     cdata.set('HAVE_LIBREADLINE', 1)
1095     editline_prefix = {
1096       'header_prefix': 'editline/',
1097       'flag_prefix': 'EDITLINE_',
1098     }
1099     readline_prefix = {
1100       'header_prefix': 'readline/',
1101       'flag_prefix': 'READLINE_',
1102     }
1103     default_prefix = {
1104       'header_prefix': '',
1105       'flag_prefix': '',
1106     }
1108     # Set the order of prefixes
1109     prefixes = libedit_preferred ? \
1110       [editline_prefix, default_prefix, readline_prefix] : \
1111       [readline_prefix, default_prefix, editline_prefix]
1113     at_least_one_header_found = false
1114     foreach header : ['history', 'readline']
1115       is_found = false
1116       foreach prefix : prefixes
1117         header_file = '@0@@1@.h'.format(prefix['header_prefix'], header)
1118         # Check history.h and readline.h
1119         if not is_found and cc.has_header(header_file,
1120             args: test_c_args, include_directories: postgres_inc,
1121             dependencies: [readline], required: false)
1122           if header == 'readline'
1123             readline_h = header_file
1124           endif
1125           cdata.set('HAVE_@0@@1@_H'.format(prefix['flag_prefix'], header).to_upper(), 1)
1126           is_found = true
1127           at_least_one_header_found = true
1128         endif
1129       endforeach
1130     endforeach
1132     if not at_least_one_header_found
1133       error('''readline header not found
1134 If you have @0@ already installed, see meson-log/meson-log.txt for details on the
1135 failure. It is possible the compiler isn't looking in the proper directory.
1136 Use -Dreadline=false to disable readline support.'''.format(readline_dep))
1137     endif
1139     check_funcs = [
1140       'append_history',
1141       'history_truncate_file',
1142       'rl_completion_matches',
1143       'rl_filename_completion_function',
1144       'rl_reset_screen_size',
1145       'rl_variable_bind',
1146     ]
1148     foreach func : check_funcs
1149       found = cc.has_function(func, dependencies: [readline],
1150         args: test_c_args, include_directories: postgres_inc)
1151       cdata.set('HAVE_'+func.to_upper(), found ? 1 : false)
1152     endforeach
1154     check_vars = [
1155       'rl_completion_suppress_quote',
1156       'rl_filename_quote_characters',
1157       'rl_filename_quoting_function',
1158     ]
1160     foreach var : check_vars
1161       cdata.set('HAVE_'+var.to_upper(),
1162         cc.has_header_symbol(readline_h, var,
1163           args: test_c_args, include_directories: postgres_inc,
1164           prefix: '#include <stdio.h>',
1165           dependencies: [readline]) ? 1 : false)
1166     endforeach
1168     # If found via cc.find_library() ensure headers are found when using the
1169     # dependency. On meson < 0.57 one cannot do compiler checks using the
1170     # dependency returned by declare_dependency(), so we can't do this above.
1171     if readline.type_name() == 'library'
1172       readline = declare_dependency(dependencies: readline,
1173         include_directories: postgres_inc)
1174     endif
1176     # On windows with mingw readline requires auto-import to successfully
1177     # link, as the headers don't use declspec(dllimport)
1178     if host_system == 'windows' and cc.get_id() != 'msvc'
1179       readline = declare_dependency(dependencies: readline,
1180         link_args: '-Wl,--enable-auto-import')
1181     endif
1182   endif
1184   # XXX: Figure out whether to implement mingw warning equivalent
1185 else
1186   readline = not_found_dep
1187 endif
1191 ###############################################################
1192 # Library: selinux
1193 ###############################################################
1195 selinux = not_found_dep
1196 selinuxopt = get_option('selinux')
1197 if meson.version().version_compare('>=0.59')
1198   selinuxopt = selinuxopt.disable_auto_if(host_system != 'linux')
1199 endif
1200 selinux = dependency('libselinux', required: selinuxopt, version: '>= 2.1.10')
1201 cdata.set('HAVE_LIBSELINUX',
1202   selinux.found() ? 1 : false)
1206 ###############################################################
1207 # Library: systemd
1208 ###############################################################
1210 systemd = not_found_dep
1211 systemdopt = get_option('systemd')
1212 if meson.version().version_compare('>=0.59')
1213   systemdopt = systemdopt.disable_auto_if(host_system != 'linux')
1214 endif
1215 systemd = dependency('libsystemd', required: systemdopt)
1216 cdata.set('USE_SYSTEMD', systemd.found() ? 1 : false)
1220 ###############################################################
1221 # Library: SSL
1222 ###############################################################
1224 ssl = not_found_dep
1225 ssl_library = 'none'
1226 sslopt = get_option('ssl')
1228 if sslopt == 'auto' and auto_features.disabled()
1229   sslopt = 'none'
1230 endif
1232 if sslopt in ['auto', 'openssl']
1233   openssl_required = (sslopt == 'openssl')
1235   # Try to find openssl via pkg-config et al, if that doesn't work
1236   # (e.g. because it's provided as part of the OS, like on FreeBSD), look for
1237   # the library names that we know about.
1239   # via pkg-config et al
1240   ssl = dependency('openssl', required: false)
1241   # only meson >= 0.57 supports declare_dependency() in cc.has_function(), so
1242   # we pass cc.find_library() results if necessary
1243   ssl_int = []
1245   # via library + headers
1246   if not ssl.found()
1247     ssl_lib = cc.find_library('ssl',
1248       dirs: test_lib_d,
1249       header_include_directories: postgres_inc,
1250       has_headers: ['openssl/ssl.h', 'openssl/err.h'],
1251       required: openssl_required)
1252     crypto_lib = cc.find_library('crypto',
1253       dirs: test_lib_d,
1254       required: openssl_required)
1255     if ssl_lib.found() and crypto_lib.found()
1256       ssl_int = [ssl_lib, crypto_lib]
1257       ssl = declare_dependency(dependencies: ssl_int, include_directories: postgres_inc)
1258     endif
1259   elif cc.has_header('openssl/ssl.h', args: test_c_args, dependencies: ssl, required: openssl_required) and \
1260        cc.has_header('openssl/err.h', args: test_c_args, dependencies: ssl, required: openssl_required)
1261     ssl_int = [ssl]
1262   else
1263     ssl = not_found_dep
1264   endif
1266   if ssl.found()
1267     check_funcs = [
1268       ['CRYPTO_new_ex_data', {'required': true}],
1269       ['SSL_new', {'required': true}],
1271       # Functions introduced in OpenSSL 1.0.2.
1272       ['X509_get_signature_nid'],
1273       ['SSL_CTX_set_cert_cb'], # not in LibreSSL
1275       # Functions introduced in OpenSSL 1.1.0. We used to check for
1276       # OPENSSL_VERSION_NUMBER, but that didn't work with 1.1.0, because LibreSSL
1277       # defines OPENSSL_VERSION_NUMBER to claim version 2.0.0, even though it
1278       # doesn't have these OpenSSL 1.1.0 functions. So check for individual
1279       # functions.
1280       ['OPENSSL_init_ssl'],
1281       ['BIO_get_data'],
1282       ['BIO_meth_new'],
1283       ['ASN1_STRING_get0_data'],
1284       ['HMAC_CTX_new'],
1285       ['HMAC_CTX_free'],
1287       # OpenSSL versions before 1.1.0 required setting callback functions, for
1288       # thread-safety. In 1.1.0, it's no longer required, and CRYPTO_lock()
1289       # function was removed.
1290       ['CRYPTO_lock'],
1292       # Function introduced in OpenSSL 1.1.1
1293       ['X509_get_signature_info'],
1294     ]
1296     are_openssl_funcs_complete = true
1297     foreach c : check_funcs
1298       func = c.get(0)
1299       val = cc.has_function(func, args: test_c_args, dependencies: ssl_int)
1300       required = c.get(1, {}).get('required', false)
1301       if required and not val
1302         are_openssl_funcs_complete = false
1303         if openssl_required
1304           error('openssl function @0@ is required'.format(func))
1305         endif
1306         break
1307       elif not required
1308         cdata.set('HAVE_' + func.to_upper(), val ? 1 : false)
1309       endif
1310     endforeach
1312     if are_openssl_funcs_complete
1313       cdata.set('USE_OPENSSL', 1,
1314                 description: 'Define to 1 to build with OpenSSL support. (-Dssl=openssl)')
1315       cdata.set('OPENSSL_API_COMPAT', '0x10001000L',
1316                 description: '''Define to the OpenSSL API version in use. This avoids deprecation warnings from newer OpenSSL versions.''')
1317       ssl_library = 'openssl'
1318     else
1319       ssl = not_found_dep
1320     endif
1321   endif
1322 endif
1324 if sslopt == 'auto' and auto_features.enabled() and not ssl.found()
1325   error('no SSL library found')
1326 endif
1330 ###############################################################
1331 # Library: uuid
1332 ###############################################################
1334 uuidopt = get_option('uuid')
1335 if uuidopt != 'none'
1336   uuidname = uuidopt.to_upper()
1337   if uuidopt == 'e2fs'
1338     uuid = dependency('uuid', required: true)
1339     uuidfunc = 'uuid_generate'
1340     uuidheader = 'uuid/uuid.h'
1341   elif uuidopt == 'bsd'
1342     # libc should have uuid function
1343     uuid = declare_dependency()
1344     uuidfunc = 'uuid_to_string'
1345     uuidheader = 'uuid.h'
1346   elif uuidopt == 'ossp'
1347     uuid = dependency('ossp-uuid', required: true)
1348     uuidfunc = 'uuid_export'
1349     uuidheader = 'uuid.h'
1350   else
1351     error('huh')
1352   endif
1354   if not cc.has_header_symbol(uuidheader, uuidfunc, args: test_c_args, dependencies: uuid)
1355     error('uuid library @0@ missing required function @1@'.format(uuidopt, uuidfunc))
1356   endif
1357   cdata.set('HAVE_@0@'.format(uuidheader.underscorify().to_upper()), 1)
1359   cdata.set('HAVE_UUID_@0@'.format(uuidname), 1,
1360            description: 'Define to 1 if you have @0@ UUID support.'.format(uuidname))
1361 else
1362   uuid = not_found_dep
1363 endif
1367 ###############################################################
1368 # Library: zlib
1369 ###############################################################
1371 zlibopt = get_option('zlib')
1372 zlib = not_found_dep
1373 if not zlibopt.disabled()
1374   zlib_t = dependency('zlib', required: zlibopt)
1376   if zlib_t.type_name() == 'internal'
1377     # if fallback was used, we don't need to test if headers are present (they
1378     # aren't built yet, so we can't test)
1379     zlib = zlib_t
1380   elif not zlib_t.found()
1381     warning('did not find zlib')
1382   elif not cc.has_header('zlib.h',
1383       args: test_c_args, include_directories: postgres_inc,
1384       dependencies: [zlib_t], required: zlibopt.enabled())
1385     warning('zlib header not found')
1386   elif not cc.has_type('z_streamp',
1387       dependencies: [zlib_t], prefix: '#include <zlib.h>',
1388       args: test_c_args, include_directories: postgres_inc)
1389     if zlibopt.enabled()
1390       error('zlib version is too old')
1391     else
1392       warning('zlib version is too old')
1393     endif
1394   else
1395     zlib = zlib_t
1396   endif
1398   if zlib.found()
1399     cdata.set('HAVE_LIBZ', 1)
1400   endif
1401 endif
1405 ###############################################################
1406 # Library: tap test dependencies
1407 ###############################################################
1409 # Check whether tap tests are enabled or not
1410 tap_tests_enabled = false
1411 tapopt = get_option('tap_tests')
1412 if not tapopt.disabled()
1413   # Checking for perl modules for tap tests
1414   perl_ipc_run_check = run_command(perl, 'config/check_modules.pl', check: false)
1415   if perl_ipc_run_check.returncode() != 0
1416     message(perl_ipc_run_check.stderr().strip())
1417     if tapopt.enabled()
1418       error('Additional Perl modules are required to run TAP tests.')
1419     else
1420       warning('Additional Perl modules are required to run TAP tests.')
1421     endif
1422   else
1423     tap_tests_enabled = true
1424   endif
1425 endif
1429 ###############################################################
1430 # Library: zstd
1431 ###############################################################
1433 zstdopt = get_option('zstd')
1434 if not zstdopt.disabled()
1435   zstd = dependency('libzstd', required: zstdopt, version: '>=1.4.0')
1437   if zstd.found()
1438     cdata.set('USE_ZSTD', 1)
1439     cdata.set('HAVE_LIBZSTD', 1)
1440   endif
1442 else
1443   zstd = not_found_dep
1444 endif
1448 ###############################################################
1449 # Compiler tests
1450 ###############################################################
1452 # Do we need -std=c99 to compile C99 code? We don't want to add -std=c99
1453 # unnecessarily, because we optionally rely on newer features.
1454 c99_test = '''
1455 #include <stdbool.h>
1456 #include <complex.h>
1457 #include <tgmath.h>
1458 #include <inttypes.h>
1460 struct named_init_test {
1461   int a;
1462   int b;
1465 extern void structfunc(struct named_init_test);
1467 int main(int argc, char **argv)
1469   struct named_init_test nit = {
1470     .a = 3,
1471     .b = 5,
1472   };
1474   for (int loop_var = 0; loop_var < 3; loop_var++)
1475   {
1476     nit.a += nit.b;
1477   }
1479   structfunc((struct named_init_test){1, 0});
1481   return nit.a != 0;
1485 if not cc.compiles(c99_test, name: 'c99', args: test_c_args)
1486   if cc.compiles(c99_test, name: 'c99 with -std=c99',
1487         args: test_c_args + ['-std=c99'])
1488     test_c_args += '-std=c99'
1489     cflags += '-std=c99'
1490   else
1491     error('C compiler does not support C99')
1492   endif
1493 endif
1495 sizeof_long = cc.sizeof('long', args: test_c_args)
1496 cdata.set('SIZEOF_LONG', sizeof_long)
1497 if sizeof_long == 8
1498   cdata.set('HAVE_LONG_INT_64', 1)
1499   cdata.set('PG_INT64_TYPE', 'long int')
1500   cdata.set_quoted('INT64_MODIFIER', 'l')
1501 elif sizeof_long == 4 and cc.sizeof('long long', args: test_c_args) == 8
1502   cdata.set('HAVE_LONG_LONG_INT_64', 1)
1503   cdata.set('PG_INT64_TYPE', 'long long int')
1504   cdata.set_quoted('INT64_MODIFIER', 'll')
1505 else
1506   error('do not know how to get a 64bit int')
1507 endif
1509 if host_machine.endian() == 'big'
1510   cdata.set('WORDS_BIGENDIAN', 1)
1511 endif
1513 alignof_types = ['short', 'int', 'long', 'double']
1514 maxalign = 0
1515 foreach t : alignof_types
1516   align = cc.alignment(t, args: test_c_args)
1517   if maxalign < align
1518     maxalign = align
1519   endif
1520   cdata.set('ALIGNOF_@0@'.format(t.to_upper()), align)
1521 endforeach
1522 cdata.set('MAXIMUM_ALIGNOF', maxalign)
1524 cdata.set('SIZEOF_VOID_P', cc.sizeof('void *', args: test_c_args))
1525 cdata.set('SIZEOF_SIZE_T', cc.sizeof('size_t', args: test_c_args))
1528 # Check if __int128 is a working 128 bit integer type, and if so
1529 # define PG_INT128_TYPE to that typename.
1531 # This currently only detects a GCC/clang extension, but support for other
1532 # environments may be added in the future.
1534 # For the moment we only test for support for 128bit math; support for
1535 # 128bit literals and snprintf is not required.
1536 if cc.links('''
1537   /*
1538    * We don't actually run this test, just link it to verify that any support
1539    * functions needed for __int128 are present.
1540    *
1541    * These are globals to discourage the compiler from folding all the
1542    * arithmetic tests down to compile-time constants.  We do not have
1543    * convenient support for 128bit literals at this point...
1544    */
1545   __int128 a = 48828125;
1546   __int128 b = 97656250;
1548   int main(void)
1549   {
1550       __int128 c,d;
1551       a = (a << 12) + 1; /* 200000000001 */
1552       b = (b << 12) + 5; /* 400000000005 */
1553       /* try the most relevant arithmetic ops */
1554       c = a * b;
1555       d = (c + b) / b;
1556       /* must use the results, else compiler may optimize arithmetic away */
1557       return d != a+1;
1558   }''',
1559   name: '__int128',
1560   args: test_c_args)
1562   buggy_int128 = false
1564   # Use of non-default alignment with __int128 tickles bugs in some compilers.
1565   # If not cross-compiling, we can test for bugs and disable use of __int128
1566   # with buggy compilers.  If cross-compiling, hope for the best.
1567   # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83925
1568   if not meson.is_cross_build()
1569     r = cc.run('''
1570     /* This must match the corresponding code in c.h: */
1571     #if defined(__GNUC__) || defined(__SUNPRO_C) || defined(__IBMC__)
1572     #define pg_attribute_aligned(a) __attribute__((aligned(a)))
1573     #elif defined(_MSC_VER)
1574     #define pg_attribute_aligned(a) __declspec(align(a))
1575     #endif
1576     typedef __int128 int128a
1577     #if defined(pg_attribute_aligned)
1578     pg_attribute_aligned(8)
1579     #endif
1580     ;
1582     int128a holder;
1583     void pass_by_val(void *buffer, int128a par) { holder = par; }
1585     int main(void)
1586     {
1587         long int i64 = 97656225L << 12;
1588         int128a q;
1589         pass_by_val(main, (int128a) i64);
1590         q = (int128a) i64;
1591         return q != holder;
1592     }''',
1593     name: '__int128 alignment bug',
1594     args: test_c_args)
1595     assert(r.compiled())
1596     if r.returncode() != 0
1597       buggy_int128 = true
1598       message('__int128 support present but buggy and thus disabled')
1599     endif
1600   endif
1602   if not buggy_int128
1603     cdata.set('PG_INT128_TYPE', '__int128')
1604     cdata.set('ALIGNOF_PG_INT128_TYPE', cc.
1605       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... Any 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 # Check for the locale_t type and find the right header file.  macOS
2287 # needs xlocale.h; standard is locale.h, but glibc also has an
2288 # xlocale.h file that we should not use.  MSVC has a replacement
2289 # defined in src/include/port/win32_port.h.
2290 if cc.has_type('locale_t', prefix: '#include <locale.h>')
2291   cdata.set('HAVE_LOCALE_T', 1)
2292 elif cc.has_type('locale_t', prefix: '#include <xlocale.h>')
2293   cdata.set('HAVE_LOCALE_T', 1)
2294   cdata.set('LOCALE_T_IN_XLOCALE', 1)
2295 elif cc.get_id() == 'msvc'
2296   cdata.set('HAVE_LOCALE_T', 1)
2297 endif
2299 # Check if the C compiler understands typeof or a variant.  Define
2300 # HAVE_TYPEOF if so, and define 'typeof' to the actual key word.
2301 foreach kw : ['typeof', '__typeof__', 'decltype']
2302   if cc.compiles('''
2303 int main(void)
2305     int x = 0;
2306     @0@(x) y;
2307     y = x;
2308     return y;
2310 '''.format(kw),
2311     name: 'typeof()',
2312     args: test_c_args, include_directories: postgres_inc)
2314     cdata.set('HAVE_TYPEOF', 1)
2315     if kw != 'typeof'
2316       cdata.set('typeof', kw)
2317     endif
2319     break
2320   endif
2321 endforeach
2324 # Try to find a declaration for wcstombs_l().  It might be in stdlib.h
2325 # (following the POSIX requirement for wcstombs()), or in locale.h, or in
2326 # xlocale.h.  If it's in the latter, define WCSTOMBS_L_IN_XLOCALE.
2327 wcstombs_l_test = '''
2328 #include <stdlib.h>
2329 #include <locale.h>
2332 void main(void)
2334 #ifndef wcstombs_l
2335     (void) wcstombs_l;
2336 #endif
2339 if (not cc.compiles(wcstombs_l_test.format(''),
2340       name: 'wcstombs_l') and
2341     cc.compiles(wcstombs_l_test.format('#include <xlocale.h>'),
2342       name: 'wcstombs_l in xlocale.h'))
2343     cdata.set('WCSTOMBS_L_IN_XLOCALE', 1)
2344 endif
2347 # MSVC doesn't cope well with defining restrict to __restrict, the spelling it
2348 # understands, because it conflicts with __declspec(restrict). Therefore we
2349 # define pg_restrict to the appropriate definition, which presumably won't
2350 # conflict.
2352 # We assume C99 support, so we don't need to make this conditional.
2354 # XXX: Historically we allowed platforms to disable restrict in template
2355 # files, but that was only added for AIX when building with XLC, which we
2356 # don't support yet.
2357 cdata.set('pg_restrict', '__restrict')
2360 # Most libraries are included only if they demonstrably provide a function we
2361 # need, but libm is an exception: always include it, because there are too
2362 # many compilers that play cute optimization games that will break probes for
2363 # standard functions such as pow().
2364 os_deps += cc.find_library('m', required: false)
2366 rt_dep = cc.find_library('rt', required: false)
2368 dl_dep = cc.find_library('dl', required: false)
2370 util_dep = cc.find_library('util', required: false)
2371 posix4_dep = cc.find_library('posix4', required: false)
2373 getopt_dep = cc.find_library('getopt', required: false)
2374 gnugetopt_dep = cc.find_library('gnugetopt', required: false)
2375 # Check if we want to replace getopt/getopt_long even if provided by the system
2376 # - Mingw has adopted a GNU-centric interpretation of optind/optreset,
2377 #   so always use our version on Windows
2378 # - On OpenBSD and Solaris, getopt() doesn't do what we want for long options
2379 #   (i.e., allow '-' as a flag character), so use our version on those platforms
2380 # - We want to use system's getopt_long() only if the system provides struct
2381 #   option
2382 always_replace_getopt = host_system in ['windows', 'cygwin', 'openbsd', 'solaris']
2383 always_replace_getopt_long = host_system in ['windows', 'cygwin'] or not cdata.has('HAVE_STRUCT_OPTION')
2385 # Required on BSDs
2386 execinfo_dep = cc.find_library('execinfo', required: false)
2388 if host_system == 'cygwin'
2389   cygipc_dep = cc.find_library('cygipc', required: false)
2390 else
2391   cygipc_dep = not_found_dep
2392 endif
2394 if host_system == 'sunos'
2395   socket_dep = cc.find_library('socket', required: false)
2396 else
2397   socket_dep = not_found_dep
2398 endif
2400 # XXX: Might be worth conditioning some checks on the OS, to avoid doing
2401 # unnecessary checks over and over, particularly on windows.
2402 func_checks = [
2403   ['_configthreadlocale', {'skip': host_system != 'windows'}],
2404   ['backtrace_symbols', {'dependencies': [execinfo_dep]}],
2405   ['clock_gettime', {'dependencies': [rt_dep, posix4_dep], 'define': false}],
2406   ['copyfile'],
2407   # gcc/clang's sanitizer helper library provides dlopen but not dlsym, thus
2408   # when enabling asan the dlopen check doesn't notice that -ldl is actually
2409   # required. Just checking for dlsym() ought to suffice.
2410   ['dlsym', {'dependencies': [dl_dep], 'define': false}],
2411   ['explicit_bzero'],
2412   ['fdatasync', {'dependencies': [rt_dep, posix4_dep], 'define': false}], # Solaris
2413   ['getifaddrs'],
2414   ['getopt', {'dependencies': [getopt_dep, gnugetopt_dep], 'skip': always_replace_getopt}],
2415   ['getopt_long', {'dependencies': [getopt_dep, gnugetopt_dep], 'skip': always_replace_getopt_long}],
2416   ['getpeereid'],
2417   ['getpeerucred'],
2418   ['inet_aton'],
2419   ['inet_pton'],
2420   ['kqueue'],
2421   ['mbstowcs_l'],
2422   ['memset_s'],
2423   ['mkdtemp'],
2424   ['posix_fadvise'],
2425   ['posix_fallocate'],
2426   ['ppoll'],
2427   ['pstat'],
2428   ['pthread_barrier_wait', {'dependencies': [thread_dep]}],
2429   ['pthread_is_threaded_np', {'dependencies': [thread_dep]}],
2430   ['sem_init', {'dependencies': [rt_dep, thread_dep], 'skip': sema_kind != 'unnamed_posix', 'define': false}],
2431   ['setproctitle', {'dependencies': [util_dep]}],
2432   ['setproctitle_fast'],
2433   ['shm_open', {'dependencies': [rt_dep], 'define': false}],
2434   ['shm_unlink', {'dependencies': [rt_dep], 'define': false}],
2435   ['shmget', {'dependencies': [cygipc_dep], 'define': false}],
2436   ['socket', {'dependencies': [socket_dep], 'define': false}],
2437   ['strchrnul'],
2438   ['strerror_r', {'dependencies': [thread_dep]}],
2439   ['strlcat'],
2440   ['strlcpy'],
2441   ['strnlen'],
2442   ['strsignal'],
2443   ['sync_file_range'],
2444   ['syncfs'],
2445   ['uselocale'],
2446   ['wcstombs_l'],
2449 func_check_results = {}
2450 foreach c : func_checks
2451   func = c.get(0)
2452   kwargs = c.get(1, {})
2453   deps = kwargs.get('dependencies', [])
2455   if kwargs.get('skip', false)
2456     continue
2457   endif
2459   found = cc.has_function(func, args: test_c_args)
2461   if not found
2462     foreach dep : deps
2463       if not dep.found()
2464         continue
2465       endif
2466       found = cc.has_function(func, args: test_c_args,
2467                               dependencies: [dep])
2468       if found
2469         os_deps += dep
2470         break
2471       endif
2472     endforeach
2473   endif
2475   func_check_results += {func: found}
2477   if kwargs.get('define', true)
2478     # Emulate autoconf behaviour of not-found->undef, found->1
2479     cdata.set('HAVE_' + func.underscorify().to_upper(),
2480               found  ? 1 : false,
2481               description: 'Define to 1 if you have the `@0@\' function.'.format(func))
2482   endif
2483 endforeach
2486 if cc.has_function('syslog', args: test_c_args) and \
2487     cc.check_header('syslog.h', args: test_c_args)
2488   cdata.set('HAVE_SYSLOG', 1)
2489 endif
2492 # MSVC has replacements defined in src/include/port/win32_port.h.
2493 if cc.get_id() == 'msvc'
2494   cdata.set('HAVE_WCSTOMBS_L', 1)
2495   cdata.set('HAVE_MBSTOWCS_L', 1)
2496 endif
2499 # if prerequisites for unnamed posix semas aren't fulfilled, fall back to sysv
2500 # semaphores
2501 if sema_kind == 'unnamed_posix' and \
2502    not func_check_results.get('sem_init', false)
2503   sema_kind = 'sysv'
2504 endif
2506 cdata.set('USE_@0@_SHARED_MEMORY'.format(shmem_kind.to_upper()), 1)
2507 cdata.set('USE_@0@_SEMAPHORES'.format(sema_kind.to_upper()), 1)
2509 cdata.set('MEMSET_LOOP_LIMIT', memset_loop_limit)
2510 cdata.set_quoted('DLSUFFIX', dlsuffix)
2513 # built later than the rest of the version metadata, we need SIZEOF_VOID_P
2514 cdata.set_quoted('PG_VERSION_STR',
2515   'PostgreSQL @0@ on @1@-@2@, compiled by @3@-@4@, @5@-bit'.format(
2516     pg_version, host_machine.cpu_family(), host_system,
2517     cc.get_id(), cc.version(), cdata.get('SIZEOF_VOID_P') * 8,
2518   )
2523 ###############################################################
2524 # Threading
2525 ###############################################################
2527 # XXX: About to rely on thread safety in the autoconf build, so not worth
2528 # implementing a fallback.
2529 cdata.set('ENABLE_THREAD_SAFETY', 1)
2533 ###############################################################
2534 # NLS / Gettext
2535 ###############################################################
2537 nlsopt = get_option('nls')
2538 libintl = not_found_dep
2540 if not nlsopt.disabled()
2541   # otherwise there'd be lots of
2542   # "Gettext not found, all translation (po) targets will be ignored."
2543   # warnings if not found.
2544   msgfmt = find_program('msgfmt', required: nlsopt.enabled(), native: true)
2546   # meson 0.59 has this wrapped in dependency('int')
2547   if (msgfmt.found() and
2548       cc.check_header('libintl.h', required: nlsopt,
2549         args: test_c_args, include_directories: postgres_inc))
2551     # in libc
2552     if cc.has_function('ngettext')
2553       libintl = declare_dependency()
2554     else
2555       libintl = cc.find_library('intl',
2556         has_headers: ['libintl.h'], required: nlsopt,
2557         header_include_directories: postgres_inc,
2558         dirs: test_lib_d)
2559     endif
2560   endif
2562   if libintl.found()
2563     i18n = import('i18n')
2564     cdata.set('ENABLE_NLS', 1)
2565   endif
2566 endif
2570 ###############################################################
2571 # Build
2572 ###############################################################
2574 # Set up compiler / linker arguments to be used everywhere, individual targets
2575 # can add further args directly, or indirectly via dependencies
2576 add_project_arguments(cflags, language: ['c'])
2577 add_project_arguments(cppflags, language: ['c'])
2578 add_project_arguments(cflags_warn, language: ['c'])
2579 add_project_arguments(cxxflags, language: ['cpp'])
2580 add_project_arguments(cppflags, language: ['cpp'])
2581 add_project_arguments(cxxflags_warn, language: ['cpp'])
2582 add_project_link_arguments(ldflags, language: ['c', 'cpp'])
2585 # Collect a number of lists of things while recursing through the source
2586 # tree. Later steps then can use those.
2588 # list of targets for various alias targets
2589 backend_targets = []
2590 bin_targets = []
2591 pl_targets = []
2592 contrib_targets = []
2593 testprep_targets = []
2594 nls_targets = []
2597 # Define the tests to distribute them to the correct test styles later
2598 test_deps = []
2599 tests = []
2602 # Default options for targets
2604 # First identify rpaths
2605 bin_install_rpaths = []
2606 lib_install_rpaths = []
2607 mod_install_rpaths = []
2610 # Don't add rpaths on darwin for now - as long as only absolute references to
2611 # libraries are needed, absolute LC_ID_DYLIB ensures libraries can be found in
2612 # their final destination.
2613 if host_system != 'darwin'
2614   # Add absolute path to libdir to rpath. This ensures installed binaries /
2615   # libraries find our libraries (mainly libpq).
2616   bin_install_rpaths += dir_prefix / dir_lib
2617   lib_install_rpaths += dir_prefix / dir_lib
2618   mod_install_rpaths += dir_prefix / dir_lib
2620   # Add extra_lib_dirs to rpath. This ensures we find libraries we depend on.
2621   #
2622   # Not needed on darwin even if we use relative rpaths for our own libraries,
2623   # as the install_name of libraries in extra_lib_dirs will point to their
2624   # location anyway.
2625   bin_install_rpaths += postgres_lib_d
2626   lib_install_rpaths += postgres_lib_d
2627   mod_install_rpaths += postgres_lib_d
2628 endif
2631 # Define arguments for default targets
2633 default_target_args = {
2634   'implicit_include_directories': false,
2635   'install': true,
2638 default_lib_args = default_target_args + {
2639   'name_prefix': '',
2642 internal_lib_args = default_lib_args + {
2643   'build_by_default': false,
2644   'install': false,
2647 default_mod_args = default_lib_args + {
2648   'name_prefix': '',
2649   'install_dir': dir_lib_pkg,
2652 default_bin_args = default_target_args + {
2653   'install_dir': dir_bin,
2656 if get_option('rpath')
2657   default_lib_args += {
2658     'install_rpath': ':'.join(lib_install_rpaths),
2659   }
2661   default_mod_args += {
2662     'install_rpath': ':'.join(mod_install_rpaths),
2663   }
2665   default_bin_args += {
2666     'install_rpath': ':'.join(bin_install_rpaths),
2667   }
2668 endif
2671 # Helper for exporting a limited number of symbols
2672 gen_export_kwargs = {
2673   'input': 'exports.txt',
2674   'output': '@BASENAME@.'+export_file_suffix,
2675   'command': [perl, files('src/tools/gen_export.pl'),
2676    '--format', export_file_format,
2677    '--input', '@INPUT0@', '--output', '@OUTPUT0@'],
2678   'build_by_default': false,
2679   'install': false,
2685 ### windows resources related stuff
2688 if host_system == 'windows'
2689   pg_ico = meson.source_root() / 'src' / 'port' / 'win32.ico'
2690   win32ver_rc = files('src/port/win32ver.rc')
2691   rcgen = find_program('src/tools/rcgen', native: true)
2693   rcgen_base_args = [
2694     '--srcdir', '@SOURCE_DIR@',
2695     '--builddir', meson.build_root(),
2696     '--rcout', '@OUTPUT0@',
2697     '--out', '@OUTPUT1@',
2698     '--input', '@INPUT@',
2699     '@EXTRA_ARGS@',
2700   ]
2702   if cc.get_argument_syntax() == 'msvc'
2703     rc = find_program('rc', required: true)
2704     rcgen_base_args += ['--rc', rc.path()]
2705     rcgen_outputs = ['@BASENAME@.rc', '@BASENAME@.res']
2706   else
2707     windres = find_program('windres', required: true)
2708     rcgen_base_args += ['--windres', windres.path()]
2709     rcgen_outputs = ['@BASENAME@.rc', '@BASENAME@.obj']
2710   endif
2712   # msbuild backend doesn't support this atm
2713   if meson.backend() == 'ninja'
2714     rcgen_base_args += ['--depfile', '@DEPFILE@']
2715   endif
2717   rcgen_bin_args = rcgen_base_args + [
2718     '--VFT_TYPE', 'VFT_APP',
2719     '--FILEENDING', 'exe',
2720     '--ICO', pg_ico
2721   ]
2723   rcgen_lib_args = rcgen_base_args + [
2724     '--VFT_TYPE', 'VFT_DLL',
2725     '--FILEENDING', 'dll',
2726   ]
2728   rc_bin_gen = generator(rcgen,
2729     depfile: '@BASENAME@.d',
2730     arguments: rcgen_bin_args,
2731     output: rcgen_outputs,
2732   )
2734   rc_lib_gen = generator(rcgen,
2735     depfile: '@BASENAME@.d',
2736     arguments: rcgen_lib_args,
2737     output: rcgen_outputs,
2738   )
2739 endif
2743 # headers that the whole build tree depends on
2744 generated_headers = []
2745 # headers that the backend build depends on
2746 generated_backend_headers = []
2747 # configure_files() output, needs a way of converting to file names
2748 configure_files = []
2750 # generated files that might conflict with a partial in-tree autoconf build
2751 generated_sources = []
2752 # same, for paths that differ between autoconf / meson builds
2753 # elements are [dir, [files]]
2754 generated_sources_ac = {}
2757 # First visit src/include - all targets creating headers are defined
2758 # within. That makes it easy to add the necessary dependencies for the
2759 # subsequent build steps.
2761 subdir('src/include')
2763 subdir('config')
2765 # Then through src/port and src/common, as most other things depend on them
2767 frontend_port_code = declare_dependency(
2768   compile_args: ['-DFRONTEND'],
2769   include_directories: [postgres_inc],
2770   dependencies: os_deps,
2773 backend_port_code = declare_dependency(
2774   compile_args: ['-DBUILDING_DLL'],
2775   include_directories: [postgres_inc],
2776   sources: [errcodes], # errcodes.h is needed due to use of ereport
2777   dependencies: os_deps,
2780 subdir('src/port')
2782 frontend_common_code = declare_dependency(
2783   compile_args: ['-DFRONTEND'],
2784   include_directories: [postgres_inc],
2785   sources: generated_headers,
2786   dependencies: [os_deps, zlib, zstd],
2789 backend_common_code = declare_dependency(
2790   compile_args: ['-DBUILDING_DLL'],
2791   include_directories: [postgres_inc],
2792   sources: generated_headers,
2793   dependencies: [os_deps, zlib, zstd],
2796 subdir('src/common')
2798 # all shared libraries should depend on shlib_code
2799 shlib_code = declare_dependency(
2800   link_args: ldflags_sl,
2803 # all static libraries not part of the backend should depend on this
2804 frontend_stlib_code = declare_dependency(
2805   include_directories: [postgres_inc],
2806   link_with: [common_static, pgport_static],
2807   sources: generated_headers,
2808   dependencies: [os_deps, libintl],
2811 # all shared libraries not part of the backend should depend on this
2812 frontend_shlib_code = declare_dependency(
2813   include_directories: [postgres_inc],
2814   link_with: [common_shlib, pgport_shlib],
2815   sources: generated_headers,
2816   dependencies: [shlib_code, os_deps, libintl],
2819 # Dependencies both for static and shared libpq
2820 libpq_deps += [
2821   thread_dep,
2823   gssapi,
2824   ldap_r,
2825   libintl,
2826   ssl,
2829 subdir('src/interfaces/libpq')
2830 # fe_utils depends on libpq
2831 subdir('src/fe_utils')
2833 # for frontend binaries
2834 frontend_code = declare_dependency(
2835   include_directories: [postgres_inc],
2836   link_with: [fe_utils, common_static, pgport_static],
2837   sources: generated_headers,
2838   dependencies: [os_deps, libintl],
2841 backend_both_deps += [
2842   thread_dep,
2843   bsd_auth,
2844   gssapi,
2845   icu,
2846   icu_i18n,
2847   ldap,
2848   libintl,
2849   libxml,
2850   lz4,
2851   pam,
2852   ssl,
2853   systemd,
2854   zlib,
2855   zstd,
2858 backend_mod_deps = backend_both_deps + os_deps
2860 backend_code = declare_dependency(
2861   compile_args: ['-DBUILDING_DLL'],
2862   include_directories: [postgres_inc],
2863   link_args: ldflags_be,
2864   link_with: [],
2865   sources: generated_headers + generated_backend_headers,
2866   dependencies: os_deps + backend_both_deps + backend_deps,
2869 # install these files only during test, not main install
2870 test_install_data = []
2871 test_install_libs = []
2873 # src/backend/meson.build defines backend_mod_code used for extension
2874 # libraries.
2877 # Then through the main sources. That way contrib can have dependencies on
2878 # main sources. Note that this explicitly doesn't enter src/test, right now a
2879 # few regression tests depend on contrib files.
2881 subdir('src')
2883 subdir('contrib')
2885 subdir('src/test')
2886 subdir('src/interfaces/libpq/test')
2887 subdir('src/interfaces/ecpg/test')
2889 subdir('doc/src/sgml')
2891 generated_sources_ac += {'': ['GNUmakefile']}
2893 # After processing src/test, add test_install_libs to the testprep_targets
2894 # to build them
2895 testprep_targets += test_install_libs
2898 # If there are any files in the source directory that we also generate in the
2899 # build directory, they might get preferred over the newly generated files,
2900 # e.g. because of a #include "file", which always will search in the current
2901 # directory first.
2902 message('checking for file conflicts between source and build directory')
2903 conflicting_files = []
2904 potentially_conflicting_files_t = []
2905 potentially_conflicting_files_t += generated_headers
2906 potentially_conflicting_files_t += generated_backend_headers
2907 potentially_conflicting_files_t += generated_backend_sources
2908 potentially_conflicting_files_t += generated_sources
2910 potentially_conflicting_files = []
2912 # convert all sources of potentially conflicting files into uniform shape
2913 foreach t : potentially_conflicting_files_t
2914   potentially_conflicting_files += t.full_path()
2915 endforeach
2916 foreach t : configure_files
2917   t = '@0@'.format(t)
2918   potentially_conflicting_files += meson.current_build_dir() / t
2919 endforeach
2920 foreach sub, fnames : generated_sources_ac
2921   sub = meson.build_root() / sub
2922   foreach fname : fnames
2923     potentially_conflicting_files += sub / fname
2924   endforeach
2925 endforeach
2927 # find and report conflicting files
2928 foreach build_path : potentially_conflicting_files
2929   build_path = host_system == 'windows' ? fs.as_posix(build_path) : build_path
2930   # str.replace is in 0.56
2931   src_path = meson.current_source_dir() / build_path.split(meson.current_build_dir() / '')[1]
2932   if fs.exists(src_path) or fs.is_symlink(src_path)
2933     conflicting_files += src_path
2934   endif
2935 endforeach
2936 # XXX: Perhaps we should generate a file that would clean these up? The list
2937 # can be long.
2938 if conflicting_files.length() > 0
2939   errmsg_cleanup = '''
2940 Conflicting files in source directory:
2941   @0@
2943 The conflicting files need to be removed, either by removing the files listed
2944 above, or by running configure and then make maintainer-clean.
2946   errmsg_cleanup = errmsg_cleanup.format(' '.join(conflicting_files))
2947   error(errmsg_nonclean_base.format(errmsg_cleanup))
2948 endif
2952 ###############################################################
2953 # Install targets
2954 ###############################################################
2957 # We want to define additional install targets beyond what meson provides. For
2958 # that we need to define targets depending on nearly everything. We collected
2959 # the results of i18n.gettext() invocations into nls_targets, that also
2960 # includes maintainer targets though. Collect the ones we want as a dependency.
2962 # i18n.gettext() doesn't return the dependencies before 0.60 - but the gettext
2963 # generation happens during install, so that's not a real issue.
2964 nls_mo_targets = []
2965 if libintl.found() and meson.version().version_compare('>=0.60')
2966   # use range() to avoid the flattening of the list that foreach() would do
2967   foreach off : range(0, nls_targets.length())
2968     # i18n.gettext() list containing 1) list of built .mo files 2) maintainer
2969     # -pot target 3) maintainer -pot target
2970     nls_mo_targets += nls_targets[off][0]
2971   endforeach
2972   alias_target('nls', nls_mo_targets)
2973 endif
2976 all_built = [
2977   backend_targets,
2978   bin_targets,
2979   libpq_st,
2980   pl_targets,
2981   contrib_targets,
2982   nls_mo_targets,
2983   testprep_targets,
2984   ecpg_targets,
2987 # Meson's default install target is quite verbose. Provide one that is quiet.
2988 install_quiet = custom_target('install-quiet',
2989   output: 'install-quiet',
2990   build_always_stale: true,
2991   build_by_default: false,
2992   command: [meson_bin, meson_args, 'install', '--quiet', '--no-rebuild'],
2993   depends: all_built,
2996 # Target to install files used for tests, which aren't installed by default
2997 install_test_files_args = [
2998   install_files,
2999   '--prefix', dir_prefix,
3000   '--install', contrib_data_dir, test_install_data,
3001   '--install', dir_lib_pkg, test_install_libs,
3003 run_target('install-test-files',
3004   command: [python] + install_test_files_args,
3005   depends: testprep_targets,
3010 ###############################################################
3011 # Test prep
3012 ###############################################################
3014 # DESTDIR for the installation we'll run tests in
3015 test_install_destdir = meson.build_root() / 'tmp_install/'
3017 # DESTDIR + prefix appropriately munged
3018 if build_system != 'windows'
3019   # On unixoid systems this is trivial, we just prepend the destdir
3020   assert(dir_prefix.startswith('/')) # enforced by meson
3021   test_install_location = '@0@@1@'.format(test_install_destdir, dir_prefix)
3022 else
3023   # drives, drive-relative paths, etc make this complicated on windows, call
3024   # into a copy of meson's logic for it
3025   command = [
3026     python, '-c',
3027     'import sys; from pathlib import PurePath; d1=sys.argv[1]; d2=sys.argv[2]; print(str(PurePath(d1, *PurePath(d2).parts[1:])))',
3028     test_install_destdir, dir_prefix]
3029   test_install_location = run_command(command, check: true).stdout().strip()
3030 endif
3032 meson_install_args = meson_args + ['install'] + {
3033     'meson': ['--quiet', '--only-changed', '--no-rebuild'],
3034     'muon': []
3035 }[meson_impl]
3037 # setup tests should  be run first,
3038 # so define priority for these
3039 setup_tests_priority = 100
3040 test('tmp_install',
3041     meson_bin, args: meson_install_args ,
3042     env: {'DESTDIR':test_install_destdir},
3043     priority: setup_tests_priority,
3044     timeout: 300,
3045     is_parallel: false,
3046     suite: ['setup'])
3048 test('install_test_files',
3049     python,
3050     args: install_test_files_args + ['--destdir', test_install_destdir],
3051     priority: setup_tests_priority,
3052     is_parallel: false,
3053     suite: ['setup'])
3055 test_result_dir = meson.build_root() / 'testrun'
3058 # XXX: pg_regress doesn't assign unique ports on windows. To avoid the
3059 # inevitable conflicts from running tests in parallel, hackishly assign
3060 # different ports for different tests.
3062 testport = 40000
3064 test_env = environment()
3066 temp_install_bindir = test_install_location / get_option('bindir')
3067 test_env.set('PG_REGRESS', pg_regress.full_path())
3068 test_env.set('REGRESS_SHLIB', regress_module.full_path())
3070 # Test suites that are not safe by default but can be run if selected
3071 # by the user via the whitespace-separated list in variable PG_TEST_EXTRA.
3072 # Export PG_TEST_EXTRA so it can be checked in individual tap tests.
3073 test_env.set('PG_TEST_EXTRA', get_option('PG_TEST_EXTRA'))
3075 # Add the temporary installation to the library search path on platforms where
3076 # that works (everything but windows, basically). On windows everything
3077 # library-like gets installed into bindir, solving that issue.
3078 if library_path_var != ''
3079   test_env.prepend(library_path_var, test_install_location / get_option('libdir'))
3080 endif
3084 ###############################################################
3085 # Test Generation
3086 ###############################################################
3088 # When using a meson version understanding exclude_suites, define a
3089 # 'tmp_install' test setup (the default) that excludes tests running against a
3090 # pre-existing install and a 'running' setup that conflicts with creation of
3091 # the temporary installation and tap tests (which don't support running
3092 # against a running server).
3094 running_suites = []
3095 install_suites = []
3096 if meson.version().version_compare('>=0.57')
3097   runningcheck = true
3098 else
3099   runningcheck = false
3100 endif
3102 testwrap = files('src/tools/testwrap')
3104 foreach test_dir : tests
3105   testwrap_base = [
3106     testwrap,
3107     '--basedir', meson.build_root(),
3108     '--srcdir', test_dir['sd'],
3109   ]
3111   foreach kind, v : test_dir
3112     if kind in ['sd', 'bd', 'name']
3113       continue
3114     endif
3116     t = test_dir[kind]
3118     if kind in ['regress', 'isolation', 'ecpg']
3119       if kind == 'regress'
3120         runner = pg_regress
3121         fallback_dbname = 'regression_@0@'
3122       elif kind == 'isolation'
3123         runner = pg_isolation_regress
3124         fallback_dbname = 'isolation_regression_@0@'
3125       elif kind == 'ecpg'
3126         runner = pg_regress_ecpg
3127         fallback_dbname = 'ecpg_regression_@0@'
3128       endif
3130       test_group = test_dir['name']
3131       test_group_running = test_dir['name'] + '-running'
3133       test_output = test_result_dir / test_group / kind
3134       test_output_running = test_result_dir / test_group_running/ kind
3136       # Unless specified by the test, choose a non-conflicting database name,
3137       # to avoid conflicts when running against existing server.
3138       dbname = t.get('dbname',
3139         fallback_dbname.format(test_dir['name']))
3141       test_command_base = [
3142         runner.full_path(),
3143         '--inputdir', t.get('inputdir', test_dir['sd']),
3144         '--expecteddir', t.get('expecteddir', test_dir['sd']),
3145         '--bindir', '',
3146         '--dlpath', test_dir['bd'],
3147         '--max-concurrent-tests=20',
3148         '--dbname', dbname,
3149       ] + t.get('regress_args', [])
3151       test_selection = []
3152       if t.has_key('schedule')
3153         test_selection += ['--schedule', t['schedule'],]
3154       endif
3156       if kind == 'isolation'
3157         test_selection += t.get('specs', [])
3158       else
3159         test_selection += t.get('sql', [])
3160       endif
3162       env = test_env
3163       env.prepend('PATH', temp_install_bindir, test_dir['bd'])
3165       test_kwargs = {
3166         'protocol': 'tap',
3167         'priority': 10,
3168         'timeout': 1000,
3169         'depends': test_deps + t.get('deps', []),
3170         'env': env,
3171       } + t.get('test_kwargs', {})
3173       test(test_group / kind,
3174         python,
3175         args: [
3176           testwrap_base,
3177           '--testgroup', test_group,
3178           '--testname', kind,
3179           '--',
3180           test_command_base,
3181           '--outputdir', test_output,
3182           '--temp-instance', test_output / 'tmp_check',
3183           '--port', testport.to_string(),
3184           test_selection,
3185         ],
3186         suite: test_group,
3187         kwargs: test_kwargs,
3188       )
3189       install_suites += test_group
3191       # some tests can't support running against running DB
3192       if runningcheck and t.get('runningcheck', true)
3193         test(test_group_running / kind,
3194           python,
3195           args: [
3196             testwrap_base,
3197             '--testgroup', test_group_running,
3198             '--testname', kind,
3199             '--',
3200             test_command_base,
3201             '--outputdir', test_output_running,
3202             test_selection,
3203           ],
3204           is_parallel: t.get('runningcheck-parallel', true),
3205           suite: test_group_running,
3206           kwargs: test_kwargs,
3207         )
3208         running_suites += test_group_running
3209       endif
3211       testport += 1
3212     elif kind == 'tap'
3213       if not tap_tests_enabled
3214         continue
3215       endif
3217       test_command = [
3218         perl.path(),
3219         '-I', meson.source_root() / 'src/test/perl',
3220         '-I', test_dir['sd'],
3221       ]
3223       # Add temporary install, the build directory for non-installed binaries and
3224       # also test/ for non-installed test binaries built separately.
3225       env = test_env
3226       env.prepend('PATH', temp_install_bindir, test_dir['bd'], test_dir['bd'] / 'test')
3228       foreach name, value : t.get('env', {})
3229         env.set(name, value)
3230       endforeach
3232       test_group = test_dir['name']
3233       test_kwargs = {
3234         'protocol': 'tap',
3235         'suite': test_group,
3236         'timeout': 1000,
3237         'depends': test_deps + t.get('deps', []),
3238         'env': env,
3239       } + t.get('test_kwargs', {})
3241       foreach onetap : t['tests']
3242         # Make tap test names prettier, remove t/ and .pl
3243         onetap_p = onetap
3244         if onetap_p.startswith('t/')
3245           onetap_p = onetap.split('t/')[1]
3246         endif
3247         if onetap_p.endswith('.pl')
3248           onetap_p = fs.stem(onetap_p)
3249         endif
3251         test(test_dir['name'] / onetap_p,
3252           python,
3253           kwargs: test_kwargs,
3254           args: testwrap_base + [
3255             '--testgroup', test_dir['name'],
3256             '--testname', onetap_p,
3257             '--', test_command,
3258             test_dir['sd'] / onetap,
3259           ],
3260         )
3261       endforeach
3262       install_suites += test_group
3263     else
3264       error('unknown kind @0@ of test in @1@'.format(kind, test_dir['sd']))
3265     endif
3267   endforeach # kinds of tests
3269 endforeach # directories with tests
3271 # repeat condition so meson realizes version dependency
3272 if meson.version().version_compare('>=0.57')
3273   add_test_setup('tmp_install',
3274     is_default: true,
3275     exclude_suites: running_suites)
3276   add_test_setup('running',
3277     exclude_suites: ['setup'] + install_suites)
3278 endif
3282 ###############################################################
3283 # Pseudo targets
3284 ###############################################################
3286 alias_target('backend', backend_targets)
3287 alias_target('bin', bin_targets + [libpq_st])
3288 alias_target('pl', pl_targets)
3289 alias_target('contrib', contrib_targets)
3290 alias_target('testprep', testprep_targets)
3291 alias_target('install-world', install_quiet, installdocs)
3295 ###############################################################
3296 # The End, The End, My Friend
3297 ###############################################################
3299 if meson.version().version_compare('>=0.57')
3301   summary(
3302     {
3303       'data block size': '@0@ kB'.format(cdata.get('BLCKSZ') / 1024),
3304       'WAL block size': '@0@ kB'.format(cdata.get('XLOG_BLCKSZ') / 1024),
3305       'segment size': get_option('segsize_blocks') != 0 ?
3306         '@0@ blocks'.format(cdata.get('RELSEG_SIZE')) :
3307         '@0@ GB'.format(get_option('segsize')),
3308     },
3309     section: 'Data layout',
3310   )
3312   summary(
3313     {
3314       'host system': '@0@ @1@'.format(host_system, host_cpu),
3315       'build system': '@0@ @1@'.format(build_machine.system(),
3316                                        build_machine.cpu_family()),
3317     },
3318     section: 'System',
3319   )
3321   summary(
3322     {
3323       'linker': '@0@'.format(cc.get_linker_id()),
3324       'C compiler': '@0@ @1@'.format(cc.get_id(), cc.version()),
3325     },
3326     section: 'Compiler',
3327   )
3329   summary(
3330     {
3331       'CPP FLAGS': ' '.join(cppflags),
3332       'C FLAGS, functional': ' '.join(cflags),
3333       'C FLAGS, warnings': ' '.join(cflags_warn),
3334       'C FLAGS, modules': ' '.join(cflags_mod),
3335       'C FLAGS, user specified': ' '.join(get_option('c_args')),
3336       'LD FLAGS': ' '.join(ldflags + get_option('c_link_args')),
3337     },
3338     section: 'Compiler Flags',
3339   )
3341   if llvm.found()
3342     summary(
3343       {
3344         'C++ compiler': '@0@ @1@'.format(cpp.get_id(), cpp.version()),
3345       },
3346       section: 'Compiler',
3347     )
3349     summary(
3350       {
3351         'C++ FLAGS, functional': ' '.join(cxxflags),
3352         'C++ FLAGS, warnings': ' '.join(cxxflags_warn),
3353         'C++ FLAGS, user specified': ' '.join(get_option('cpp_args')),
3354       },
3355       section: 'Compiler Flags',
3356     )
3357   endif
3359   summary(
3360     {
3361       'bison': '@0@ @1@'.format(bison.full_path(), bison_version),
3362       'dtrace': dtrace,
3363     },
3364     section: 'Programs',
3365   )
3367   summary(
3368     {
3369       'bonjour': bonjour,
3370       'bsd_auth': bsd_auth,
3371       'docs': docs_dep,
3372       'docs_pdf': docs_pdf_dep,
3373       'gss': gssapi,
3374       'icu': icu,
3375       'ldap': ldap,
3376       'libxml': libxml,
3377       'libxslt': libxslt,
3378       'llvm': llvm,
3379       'lz4': lz4,
3380       'nls': libintl,
3381       'openssl': ssl,
3382       'pam': pam,
3383       'plperl': perl_dep,
3384       'plpython': python3_dep,
3385       'pltcl': tcl_dep,
3386       'readline': readline,
3387       'selinux': selinux,
3388       'systemd': systemd,
3389       'uuid': uuid,
3390       'zlib': zlib,
3391       'zstd': zstd,
3392     },
3393     section: 'External libraries',
3394   )
3396 endif