Fix order of operations in ExecEvalFieldStoreDeForm().
[pgsql.git] / meson.build
blob7f67966db0dbb7dde404dcb90da176e36e9e54f2
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: '16beta2',
12   license: 'PostgreSQL',
14   # We want < 0.56 for python 3.5 compatibility on old platforms. EPEL for
15   # RHEL 7 has 0.55. < 0.54 would require replacing some uses of the fs
16   # module, < 0.53 all uses of fs. So far there's no need to go to >=0.56.
17   meson_version: '>=0.54',
18   default_options: [
19     'warning_level=1', #-Wall equivalent
20     'b_pch=false',
21     'buildtype=debugoptimized', # -O2 + debug
22     # For compatibility with the autoconf build, set a default prefix. This
23     # works even on windows, where it's a drive-relative path (i.e. when on
24     # d:/somepath it'll install to d:/usr/local/pgsql)
25     'prefix=/usr/local/pgsql',
26   ]
31 ###############################################################
32 # Basic prep
33 ###############################################################
35 fs = import('fs')
36 pkgconfig = import('pkgconfig')
38 host_system = host_machine.system()
39 build_system = build_machine.system()
40 host_cpu = host_machine.cpu_family()
42 cc = meson.get_compiler('c')
44 not_found_dep = dependency('', required: false)
45 thread_dep = dependency('threads')
46 auto_features = get_option('auto_features')
50 ###############################################################
51 # Safety first
52 ###############################################################
54 # It's very easy to get into confusing states when the source directory
55 # contains an in-place build. E.g. the wrong pg_config.h will be used. So just
56 # refuse to build in that case.
58 # There's a more elaborate check later, that checks for conflicts around all
59 # generated files. But we can only do that much further down the line, so this
60 # quick check seems worth it. Adhering to this advice should clean up the
61 # conflict, but won't protect against somebody doing make distclean or just
62 # removing pg_config.h
63 errmsg_nonclean_base = '''
64 ****
65 Non-clean source code directory detected.
67 To build with meson the source tree may not have an in-place, ./configure
68 style, build configured. You can have both meson and ./configure style builds
69 for the same source tree by building out-of-source / VPATH with
70 configure. Alternatively use a separate check out for meson based builds.
72 @0@
73 ****'''
74 if fs.exists(meson.current_source_dir() / 'src' / 'include' / 'pg_config.h')
75   errmsg_cleanup = 'To clean up, run make maintainer-clean in the source tree.'
76   error(errmsg_nonclean_base.format(errmsg_cleanup))
77 endif
81 ###############################################################
82 # Variables to be determined
83 ###############################################################
85 postgres_inc_d = ['src/include']
86 postgres_inc_d += get_option('extra_include_dirs')
88 postgres_lib_d = get_option('extra_lib_dirs')
90 cppflags = []
92 cflags = []
93 cxxflags = []
94 cflags_warn = []
95 cxxflags_warn = []
96 cflags_mod = []
97 cxxflags_mod = []
99 ldflags = []
100 ldflags_be = []
101 ldflags_sl = []
102 ldflags_mod = []
104 test_c_args = []
106 os_deps = []
107 backend_both_deps = []
108 backend_deps = []
109 libpq_deps = []
111 pg_sysroot = ''
113 # source of data for pg_config.h etc
114 cdata = configuration_data()
118 ###############################################################
119 # Version and other metadata
120 ###############################################################
122 pg_version = meson.project_version()
124 if pg_version.endswith('devel')
125   pg_version_arr = [pg_version.split('devel')[0], '0']
126 elif pg_version.contains('beta')
127   pg_version_arr = [pg_version.split('beta')[0], '0']
128 elif pg_version.contains('rc')
129   pg_version_arr = [pg_version.split('rc')[0], '0']
130 else
131   pg_version_arr = pg_version.split('.')
132 endif
134 pg_version_major = pg_version_arr[0].to_int()
135 pg_version_minor = pg_version_arr[1].to_int()
136 pg_version_num = (pg_version_major * 10000) + pg_version_minor
138 pg_url = 'https://www.postgresql.org/'
140 cdata.set_quoted('PACKAGE_NAME', 'PostgreSQL')
141 cdata.set_quoted('PACKAGE_BUGREPORT', 'pgsql-bugs@lists.postgresql.org')
142 cdata.set_quoted('PACKAGE_URL', pg_url)
143 cdata.set_quoted('PACKAGE_VERSION', pg_version)
144 cdata.set_quoted('PACKAGE_STRING', 'PostgreSQL @0@'.format(pg_version))
145 cdata.set_quoted('PACKAGE_TARNAME', 'postgresql')
147 pg_version += get_option('extra_version')
148 cdata.set_quoted('PG_VERSION', pg_version)
149 cdata.set_quoted('PG_MAJORVERSION', pg_version_major.to_string())
150 cdata.set('PG_MAJORVERSION_NUM', pg_version_major)
151 cdata.set('PG_MINORVERSION_NUM', pg_version_minor)
152 cdata.set('PG_VERSION_NUM', pg_version_num)
153 # PG_VERSION_STR is built later, it depends on compiler test results
154 cdata.set_quoted('CONFIGURE_ARGS', '')
158 ###############################################################
159 # Basic platform specific configuration
160 ###############################################################
162 # meson's system names don't quite map to our "traditional" names. In some
163 # places we need the "traditional" name, e.g., for mapping
164 # src/include/port/$os.h to src/include/pg_config_os.h. Define portname for
165 # that purpose.
166 portname = host_system
168 exesuffix = '' # overridden below where necessary
169 dlsuffix = '.so' # overridden below where necessary
170 library_path_var = 'LD_LIBRARY_PATH'
172 # Format of file to control exports from libraries, and how to pass them to
173 # the compiler. For export_fmt @0@ is the path to the file export file.
174 export_file_format = 'gnu'
175 export_file_suffix = 'list'
176 export_fmt = '-Wl,--version-script=@0@'
178 # Flags to add when linking a postgres extension, @0@ is path to
179 # the relevant object on the platform.
180 mod_link_args_fmt = []
182 memset_loop_limit = 1024
184 # Choice of shared memory and semaphore implementation
185 shmem_kind = 'sysv'
186 sema_kind = 'sysv'
188 # We implement support for some operating systems by pretending they're
189 # another. Map here, before determining system properties below
190 if host_system == 'dragonfly'
191   # apparently the most similar
192   host_system = 'netbsd'
193 endif
195 if host_system == 'aix'
196   library_path_var = 'LIBPATH'
198   export_file_format = 'aix'
199   export_fmt = '-Wl,-bE:@0@'
200   mod_link_args_fmt = ['-Wl,-bI:@0@']
201   mod_link_with_dir = 'libdir'
202   mod_link_with_name = '@0@.imp'
204   # M:SRE sets a flag indicating that an object is a shared library. Seems to
205   # work in some circumstances without, but required in others.
206   ldflags_sl += '-Wl,-bM:SRE'
207   ldflags_be += '-Wl,-brtllib'
209   # Native memset() is faster, tested on:
210   # - AIX 5.1 and 5.2, XLC 6.0 (IBM's cc)
211   # - AIX 5.3 ML3, gcc 4.0.1
212   memset_loop_limit = 0
214 elif host_system == 'cygwin'
215   sema_kind = 'unnamed_posix'
216   cppflags += '-D_GNU_SOURCE'
217   dlsuffix = '.dll'
218   mod_link_args_fmt = ['@0@']
219   mod_link_with_name = 'lib@0@.exe.a'
220   mod_link_with_dir = 'libdir'
222 elif host_system == 'darwin'
223   dlsuffix = '.dylib'
224   library_path_var = 'DYLD_LIBRARY_PATH'
226   export_file_format = 'darwin'
227   export_fmt = '-exported_symbols_list=@0@'
229   mod_link_args_fmt = ['-bundle_loader', '@0@']
230   mod_link_with_dir = 'bindir'
231   mod_link_with_name = '@0@'
233   sysroot_args = [files('src/tools/darwin_sysroot'), get_option('darwin_sysroot')]
234   pg_sysroot = run_command(sysroot_args, check:true).stdout().strip()
235   message('darwin sysroot: @0@'.format(pg_sysroot))
236   if pg_sysroot != ''
237     cflags += ['-isysroot', pg_sysroot]
238     ldflags += ['-isysroot', pg_sysroot]
239   endif
240   # meson defaults to -Wl,-undefined,dynamic_lookup for modules, which we
241   # don't want because a) it's different from what we do for autoconf, b) it
242   # causes warnings starting in macOS Ventura
243   ldflags_mod += ['-Wl,-undefined,error']
245 elif host_system == 'freebsd'
246   sema_kind = 'unnamed_posix'
248 elif host_system == 'linux'
249   sema_kind = 'unnamed_posix'
250   cppflags += '-D_GNU_SOURCE'
252 elif host_system == 'netbsd'
253   # We must resolve all dynamic linking in the core server at program start.
254   # Otherwise the postmaster can self-deadlock due to signals interrupting
255   # resolution of calls, since NetBSD's linker takes a lock while doing that
256   # and some postmaster signal handlers do things that will also acquire that
257   # lock.  As long as we need "-z now", might as well specify "-z relro" too.
258   # While there's not a hard reason to adopt these settings for our other
259   # executables, there's also little reason not to, so just add them to
260   # LDFLAGS.
261   ldflags += ['-Wl,-z,now', '-Wl,-z,relro']
263 elif host_system == 'openbsd'
264   # you're ok
266 elif host_system == 'sunos'
267   portname = 'solaris'
268   export_fmt = '-Wl,-M@0@'
269   cppflags += '-D_POSIX_PTHREAD_SEMANTICS'
271 elif host_system == 'windows'
272   portname = 'win32'
273   exesuffix = '.exe'
274   dlsuffix = '.dll'
275   library_path_var = ''
277   export_file_format = 'win'
278   export_file_suffix = 'def'
279   if cc.get_id() == 'msvc'
280     export_fmt = '/DEF:@0@'
281     mod_link_with_name = '@0@.exe.lib'
282   else
283     export_fmt = '@0@'
284     mod_link_with_name = 'lib@0@.exe.a'
285   endif
286   mod_link_args_fmt = ['@0@']
287   mod_link_with_dir = 'libdir'
289   shmem_kind = 'win32'
290   sema_kind = 'win32'
292   cdata.set('WIN32_STACK_RLIMIT', 4194304)
293   if cc.get_id() == 'msvc'
294     ldflags += '/INCREMENTAL:NO'
295     ldflags += '/STACK:@0@'.format(cdata.get('WIN32_STACK_RLIMIT'))
296     # ldflags += '/nxcompat' # generated by msbuild, should have it for ninja?
297   else
298     ldflags += '-Wl,--stack,@0@'.format(cdata.get('WIN32_STACK_RLIMIT'))
299     # Need to allow multiple definitions, we e.g. want to override getopt.
300     ldflags += '-Wl,--allow-multiple-definition'
301     # Ensure we get MSVC-like linking behavior.
302     ldflags += '-Wl,--disable-auto-import'
303   endif
305   os_deps += cc.find_library('ws2_32', required: true)
306   secur32_dep = cc.find_library('secur32', required: true)
307   backend_deps += secur32_dep
308   libpq_deps += secur32_dep
310   postgres_inc_d += 'src/include/port/win32'
311   if cc.get_id() == 'msvc'
312     postgres_inc_d += 'src/include/port/win32_msvc'
313   endif
315   windows = import('windows')
317 else
318   # XXX: Should we add an option to override the host_system as an escape
319   # hatch?
320   error('unknown host system: @0@'.format(host_system))
321 endif
325 ###############################################################
326 # Program paths
327 ###############################################################
329 # External programs
330 perl = find_program(get_option('PERL'), required: true, native: true)
331 python = find_program(get_option('PYTHON'), required: true, native: true)
332 flex = find_program(get_option('FLEX'), native: true, version: '>= 2.5.35')
333 bison = find_program(get_option('BISON'), native: true, version: '>= 2.3')
334 sed = find_program(get_option('SED'), 'sed', native: true)
335 prove = find_program(get_option('PROVE'), native: true, required: false)
336 tar = find_program(get_option('TAR'), native: true)
337 gzip = find_program(get_option('GZIP'), native: true)
338 program_lz4 = find_program(get_option('LZ4'), native: true, required: false)
339 openssl = find_program(get_option('OPENSSL'), native: true, required: false)
340 program_zstd = find_program(get_option('ZSTD'), native: true, required: false)
341 dtrace = find_program(get_option('DTRACE'), native: true, required: get_option('dtrace'))
342 missing = find_program('config/missing', native: true)
343 cp = find_program('cp', required: false, native: true)
344 xmllint_bin = find_program(get_option('XMLLINT'), native: true, required: false)
345 xsltproc_bin = find_program(get_option('XSLTPROC'), native: true, required: false)
347 bison_flags = []
348 if bison.found()
349   bison_version_c = run_command(bison, '--version', check: true)
350   # bison version string helpfully is something like
351   # >>bison (GNU bison) 3.8.1<<
352   bison_version = bison_version_c.stdout().split(' ')[3].split('\n')[0]
353   if bison_version.version_compare('>=3.0')
354     bison_flags += ['-Wno-deprecated']
355   endif
356 endif
357 bison_cmd = [bison, bison_flags, '-o', '@OUTPUT0@', '-d', '@INPUT@']
358 bison_kw = {
359   'output': ['@BASENAME@.c', '@BASENAME@.h'],
360   'command': bison_cmd,
363 flex_flags = []
364 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 = not_found_dep
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   if ldap.found() and cc.has_function('ldap_initialize',
726       dependencies: ldap, args: test_c_args)
727     cdata.set('HAVE_LDAP_INITIALIZE', 1)
728   endif
729 endif
731 if ldap.found()
732   assert(ldap_r.found())
733   cdata.set('USE_LDAP', 1)
734 else
735   assert(not ldap_r.found())
736 endif
740 ###############################################################
741 # Library: LLVM
742 ###############################################################
744 llvmopt = get_option('llvm')
745 if not llvmopt.disabled()
746   add_languages('cpp', required: true, native: false)
747   llvm = dependency('llvm', version: '>=3.9', method: 'config-tool', required: llvmopt)
749   if llvm.found()
751     cdata.set('USE_LLVM', 1)
753     cpp = meson.get_compiler('cpp')
755     llvm_binpath = llvm.get_variable(configtool: 'bindir')
757     ccache = find_program('ccache', native: true, required: false)
758     clang = find_program(llvm_binpath / 'clang', required: true)
759   endif
760 else
761   llvm = not_found_dep
762 endif
766 ###############################################################
767 # Library: icu
768 ###############################################################
770 icuopt = get_option('icu')
771 if not icuopt.disabled()
772   icu = dependency('icu-uc', required: icuopt.enabled())
773   icu_i18n = dependency('icu-i18n', required: icuopt.enabled())
775   if icu.found()
776     cdata.set('USE_ICU', 1)
777   endif
779 else
780   icu = not_found_dep
781   icu_i18n = not_found_dep
782 endif
786 ###############################################################
787 # Library: libxml
788 ###############################################################
790 libxmlopt = get_option('libxml')
791 if not libxmlopt.disabled()
792   libxml = dependency('libxml-2.0', required: libxmlopt, version: '>= 2.6.23')
794   if libxml.found()
795     cdata.set('USE_LIBXML', 1)
796   endif
797 else
798   libxml = not_found_dep
799 endif
803 ###############################################################
804 # Library: libxslt
805 ###############################################################
807 libxsltopt = get_option('libxslt')
808 if not libxsltopt.disabled()
809   libxslt = dependency('libxslt', required: libxsltopt)
811   if libxslt.found()
812     cdata.set('USE_LIBXSLT', 1)
813   endif
814 else
815   libxslt = not_found_dep
816 endif
820 ###############################################################
821 # Library: lz4
822 ###############################################################
824 lz4opt = get_option('lz4')
825 if not lz4opt.disabled()
826   lz4 = dependency('liblz4', required: lz4opt)
828   if lz4.found()
829     cdata.set('USE_LZ4', 1)
830     cdata.set('HAVE_LIBLZ4', 1)
831   endif
833 else
834   lz4 = not_found_dep
835 endif
839 ###############################################################
840 # Library: Tcl (for pltcl)
842 # NB: tclConfig.sh is used in autoconf build for getting
843 # TCL_SHARED_BUILD, TCL_INCLUDE_SPEC, TCL_LIBS and TCL_LIB_SPEC
844 # variables. For now we have not seen a need to copy
845 # that behaviour to the meson build.
846 ###############################################################
848 tclopt = get_option('pltcl')
849 tcl_version = get_option('tcl_version')
850 tcl_dep = not_found_dep
851 if not tclopt.disabled()
853   # via pkg-config
854   tcl_dep = dependency(tcl_version, required: false)
856   if not tcl_dep.found()
857     tcl_dep = cc.find_library(tcl_version,
858       required: tclopt,
859       dirs: test_lib_d)
860   endif
862   if not cc.has_header('tcl.h', dependencies: tcl_dep, required: tclopt)
863     tcl_dep = not_found_dep
864   endif
865 endif
869 ###############################################################
870 # Library: pam
871 ###############################################################
873 pamopt = get_option('pam')
874 if not pamopt.disabled()
875   pam = dependency('pam', required: false)
877   if not pam.found()
878     pam = cc.find_library('pam', required: pamopt, dirs: test_lib_d)
879   endif
881   if pam.found()
882     pam_header_found = false
884     # header file <security/pam_appl.h> or <pam/pam_appl.h> is required for PAM.
885     if cc.check_header('security/pam_appl.h', dependencies: pam, required: false,
886         args: test_c_args, include_directories: postgres_inc)
887       cdata.set('HAVE_SECURITY_PAM_APPL_H', 1)
888       pam_header_found = true
889     elif cc.check_header('pam/pam_appl.h', dependencies: pam, required: pamopt,
890         args: test_c_args, include_directories: postgres_inc)
891       cdata.set('HAVE_PAM_PAM_APPL_H', 1)
892       pam_header_found = true
893     endif
895     if pam_header_found
896       cdata.set('USE_PAM', 1)
897     else
898       pam = not_found_dep
899     endif
900   endif
901 else
902   pam = not_found_dep
903 endif
907 ###############################################################
908 # Library: Perl (for plperl)
909 ###############################################################
911 perlopt = get_option('plperl')
912 perl_dep = not_found_dep
913 if not perlopt.disabled()
914   perl_may_work = true
916   # First verify that perl has the necessary dependencies installed
917   perl_mods = run_command(
918     [perl,
919      '-MConfig', '-MOpcode', '-MExtUtils::Embed', '-MExtUtils::ParseXS',
920      '-e', ''],
921     check: false)
922   if perl_mods.returncode() != 0
923     perl_may_work = false
924     perl_msg = 'perl installation does not have the required modules'
925   endif
927   # Then inquire perl about its configuration
928   if perl_may_work
929     perl_conf_cmd = [perl, '-MConfig', '-e', 'print $Config{$ARGV[0]}']
930     perlversion = run_command(perl_conf_cmd, 'api_versionstring', check: true).stdout()
931     archlibexp = run_command(perl_conf_cmd, 'archlibexp', check: true).stdout()
932     privlibexp = run_command(perl_conf_cmd, 'privlibexp', check: true).stdout()
933     useshrplib = run_command(perl_conf_cmd, 'useshrplib', check: true).stdout()
935     perl_inc_dir = '@0@/CORE'.format(archlibexp)
937     if perlversion.version_compare('< 5.14')
938       perl_may_work = false
939       perl_msg = 'Perl version 5.14 or later is required, but this is @0@'.format(perlversion)
940     elif useshrplib != 'true'
941       perl_may_work = false
942       perl_msg = 'need a shared perl'
943     endif
944   endif
946   if perl_may_work
947     # On most platforms, archlibexp is also where the Perl include files live ...
948     perl_ccflags = ['-I@0@'.format(perl_inc_dir)]
949     # ... but on newer macOS versions, we must use -iwithsysroot to look
950     # under sysroot
951     if not fs.is_file('@0@/perl.h'.format(perl_inc_dir)) and \
952        fs.is_file('@0@@1@/perl.h'.format(pg_sysroot, perl_inc_dir))
953       perl_ccflags = ['-iwithsysroot', perl_inc_dir]
954     endif
956     # check compiler finds header
957     if not cc.has_header('perl.h', required: false,
958         args: test_c_args + perl_ccflags, include_directories: postgres_inc)
959       perl_may_work = false
960       perl_msg = 'missing perl.h'
961     endif
962   endif
964   if perl_may_work
965     perl_ccflags_r = run_command(perl_conf_cmd, 'ccflags', check: true).stdout()
967     # See comments for PGAC_CHECK_PERL_EMBED_CCFLAGS in perl.m4
968     foreach flag : perl_ccflags_r.split(' ')
969       if flag.startswith('-D') and \
970           (not flag.startswith('-D_') or flag == '_USE_32BIT_TIME_T')
971         perl_ccflags += flag
972       endif
973     endforeach
975     if host_system == 'windows'
976       perl_ccflags += ['-DPLPERL_HAVE_UID_GID']
978       if cc.get_id() == 'msvc'
979         # prevent binary mismatch between MSVC built plperl and Strawberry or
980         # msys ucrt perl libraries
981         perl_ccflags += ['-DNO_THREAD_SAFE_LOCALE']
982       endif
983     endif
985     message('CCFLAGS recommended by perl: @0@'.format(perl_ccflags_r))
986     message('CCFLAGS for embedding perl: @0@'.format(' '.join(perl_ccflags)))
988     # We are after Embed's ldopts, but without the subset mentioned in
989     # Config's ccdlflags and ldflags.  (Those are the choices of those who
990     # built the Perl installation, which are not necessarily appropriate
991     # for building PostgreSQL.)
992     ldopts = run_command(perl, '-MExtUtils::Embed', '-e', 'ldopts', check: true).stdout().strip()
993     undesired = run_command(perl_conf_cmd, 'ccdlflags', check: true).stdout().split()
994     undesired += run_command(perl_conf_cmd, 'ldflags', check: true).stdout().split()
996     perl_ldopts = []
997     foreach ldopt : ldopts.split(' ')
998       if ldopt == '' or ldopt in undesired
999         continue
1000       endif
1002       perl_ldopts += ldopt.strip('"')
1003     endforeach
1005     message('LDFLAGS recommended by perl: "@0@"'.format(ldopts))
1006     message('LDFLAGS for embedding perl: "@0@"'.format(' '.join(perl_ldopts)))
1008     perl_dep_int = declare_dependency(
1009       compile_args: perl_ccflags,
1010       link_args: perl_ldopts,
1011       version: perlversion,
1012     )
1014     # While we're at it, check that we can link to libperl.
1015     # On most platforms, if perl.h is there then libperl.so will be too, but
1016     # at this writing Debian packages them separately.
1017     perl_link_test = '''
1018 /* see plperl.h */
1019 #ifdef _MSC_VER
1020 #define __inline__ inline
1021 #endif
1022 #include <EXTERN.h>
1023 #include <perl.h>
1024 int main(void)
1026 perl_alloc();
1027 }'''
1028     if not cc.links(perl_link_test, name: 'libperl',
1029           args: test_c_args + perl_ccflags + perl_ldopts,
1030           include_directories: postgres_inc)
1031       perl_may_work = false
1032       perl_msg = 'missing libperl'
1033     endif
1035   endif # perl_may_work
1037   if perl_may_work
1038     perl_dep = perl_dep_int
1039   else
1040     if perlopt.enabled()
1041       error('dependency plperl failed: @0@'.format(perl_msg))
1042     else
1043       message('disabling optional dependency plperl: @0@'.format(perl_msg))
1044     endif
1045   endif
1046 endif
1050 ###############################################################
1051 # Library: Python (for plpython)
1052 ###############################################################
1054 pyopt = get_option('plpython')
1055 if not pyopt.disabled()
1056   pm = import('python')
1057   python3_inst = pm.find_installation(required: pyopt.enabled())
1058   python3_dep = python3_inst.dependency(embed: true, required: pyopt.enabled())
1059   if not cc.check_header('Python.h', dependencies: python3_dep, required: pyopt.enabled())
1060     python3_dep = not_found_dep
1061   endif
1062 else
1063   python3_dep = not_found_dep
1064 endif
1068 ###############################################################
1069 # Library: Readline
1070 ###############################################################
1072 if not get_option('readline').disabled()
1073   libedit_preferred = get_option('libedit_preferred')
1074   # Set the order of readline dependencies
1075   check_readline_deps = libedit_preferred ? \
1076     ['libedit', 'readline'] : ['readline', 'libedit']
1078   foreach readline_dep : check_readline_deps
1079     readline = dependency(readline_dep, required: false)
1080     if not readline.found()
1081       readline = cc.find_library(readline_dep,
1082         required: get_option('readline').enabled(),
1083         dirs: test_lib_d)
1084     endif
1085     if readline.found()
1086       break
1087     endif
1088   endforeach
1090   if readline.found()
1091     cdata.set('HAVE_LIBREADLINE', 1)
1093     editline_prefix = {
1094       'header_prefix': 'editline/',
1095       'flag_prefix': 'EDITLINE_',
1096     }
1097     readline_prefix = {
1098       'header_prefix': 'readline/',
1099       'flag_prefix': 'READLINE_',
1100     }
1101     default_prefix = {
1102       'header_prefix': '',
1103       'flag_prefix': '',
1104     }
1106     # Set the order of prefixes
1107     prefixes = libedit_preferred ? \
1108       [editline_prefix, default_prefix, readline_prefix] : \
1109       [readline_prefix, default_prefix, editline_prefix]
1111     at_least_one_header_found = false
1112     foreach header : ['history', 'readline']
1113       is_found = false
1114       foreach prefix : prefixes
1115         header_file = '@0@@1@.h'.format(prefix['header_prefix'], header)
1116         # Check history.h and readline.h
1117         if not is_found and cc.has_header(header_file,
1118             args: test_c_args, include_directories: postgres_inc,
1119             dependencies: [readline], required: false)
1120           if header == 'readline'
1121             readline_h = header_file
1122           endif
1123           cdata.set('HAVE_@0@@1@_H'.format(prefix['flag_prefix'], header).to_upper(), 1)
1124           is_found = true
1125           at_least_one_header_found = true
1126         endif
1127       endforeach
1128     endforeach
1130     if not at_least_one_header_found
1131       error('''readline header not found
1132 If you have @0@ already installed, see meson-log/meson-log.txt for details on the
1133 failure. It is possible the compiler isn't looking in the proper directory.
1134 Use -Dreadline=disabled to disable readline support.'''.format(readline_dep))
1135     endif
1137     check_funcs = [
1138       'append_history',
1139       'history_truncate_file',
1140       'rl_completion_matches',
1141       'rl_filename_completion_function',
1142       'rl_reset_screen_size',
1143       'rl_variable_bind',
1144     ]
1146     foreach func : check_funcs
1147       found = cc.has_function(func, dependencies: [readline],
1148         args: test_c_args, include_directories: postgres_inc)
1149       cdata.set('HAVE_' + func.to_upper(), found ? 1 : false)
1150     endforeach
1152     check_vars = [
1153       'rl_completion_suppress_quote',
1154       'rl_filename_quote_characters',
1155       'rl_filename_quoting_function',
1156     ]
1158     foreach var : check_vars
1159       cdata.set('HAVE_' + var.to_upper(),
1160         cc.has_header_symbol(readline_h, var,
1161           args: test_c_args, include_directories: postgres_inc,
1162           prefix: '#include <stdio.h>',
1163           dependencies: [readline]) ? 1 : false)
1164     endforeach
1166     # If found via cc.find_library() ensure headers are found when using the
1167     # dependency. On meson < 0.57 one cannot do compiler checks using the
1168     # dependency returned by declare_dependency(), so we can't do this above.
1169     if readline.type_name() == 'library'
1170       readline = declare_dependency(dependencies: readline,
1171         include_directories: postgres_inc)
1172     endif
1174     # On windows with mingw readline requires auto-import to successfully
1175     # link, as the headers don't use declspec(dllimport)
1176     if host_system == 'windows' and cc.get_id() != 'msvc'
1177       readline = declare_dependency(dependencies: readline,
1178         link_args: '-Wl,--enable-auto-import')
1179     endif
1180   endif
1182   # XXX: Figure out whether to implement mingw warning equivalent
1183 else
1184   readline = not_found_dep
1185 endif
1189 ###############################################################
1190 # Library: selinux
1191 ###############################################################
1193 selinux = not_found_dep
1194 selinuxopt = get_option('selinux')
1195 if meson.version().version_compare('>=0.59')
1196   selinuxopt = selinuxopt.disable_auto_if(host_system != 'linux')
1197 endif
1198 selinux = dependency('libselinux', required: selinuxopt, version: '>= 2.1.10')
1199 cdata.set('HAVE_LIBSELINUX',
1200   selinux.found() ? 1 : false)
1204 ###############################################################
1205 # Library: systemd
1206 ###############################################################
1208 systemd = not_found_dep
1209 systemdopt = get_option('systemd')
1210 if meson.version().version_compare('>=0.59')
1211   systemdopt = systemdopt.disable_auto_if(host_system != 'linux')
1212 endif
1213 systemd = dependency('libsystemd', required: systemdopt)
1214 cdata.set('USE_SYSTEMD', systemd.found() ? 1 : false)
1218 ###############################################################
1219 # Library: SSL
1220 ###############################################################
1222 ssl = not_found_dep
1223 ssl_library = 'none'
1224 sslopt = get_option('ssl')
1226 if sslopt == 'auto' and auto_features.disabled()
1227   sslopt = 'none'
1228 endif
1230 if sslopt in ['auto', 'openssl']
1231   openssl_required = (sslopt == 'openssl')
1233   # Try to find openssl via pkg-config et al, if that doesn't work
1234   # (e.g. because it's provided as part of the OS, like on FreeBSD), look for
1235   # the library names that we know about.
1237   # via pkg-config et al
1238   ssl = dependency('openssl', required: false)
1239   # only meson >= 0.57 supports declare_dependency() in cc.has_function(), so
1240   # we pass cc.find_library() results if necessary
1241   ssl_int = []
1243   # via library + headers
1244   if not ssl.found()
1245     ssl_lib = cc.find_library('ssl',
1246       dirs: test_lib_d,
1247       header_include_directories: postgres_inc,
1248       has_headers: ['openssl/ssl.h', 'openssl/err.h'],
1249       required: openssl_required)
1250     crypto_lib = cc.find_library('crypto',
1251       dirs: test_lib_d,
1252       required: openssl_required)
1253     if ssl_lib.found() and crypto_lib.found()
1254       ssl_int = [ssl_lib, crypto_lib]
1255       ssl = declare_dependency(dependencies: ssl_int, include_directories: postgres_inc)
1256     endif
1257   elif cc.has_header('openssl/ssl.h', args: test_c_args, dependencies: ssl, required: openssl_required) and \
1258        cc.has_header('openssl/err.h', args: test_c_args, dependencies: ssl, required: openssl_required)
1259     ssl_int = [ssl]
1260   else
1261     ssl = not_found_dep
1262   endif
1264   if ssl.found()
1265     check_funcs = [
1266       ['CRYPTO_new_ex_data', {'required': true}],
1267       ['SSL_new', {'required': true}],
1269       # Functions introduced in OpenSSL 1.0.2.
1270       ['X509_get_signature_nid'],
1271       ['SSL_CTX_set_cert_cb'], # not in LibreSSL
1273       # Functions introduced in OpenSSL 1.1.0. We used to check for
1274       # OPENSSL_VERSION_NUMBER, but that didn't work with 1.1.0, because LibreSSL
1275       # defines OPENSSL_VERSION_NUMBER to claim version 2.0.0, even though it
1276       # doesn't have these OpenSSL 1.1.0 functions. So check for individual
1277       # functions.
1278       ['OPENSSL_init_ssl'],
1279       ['BIO_get_data'],
1280       ['BIO_meth_new'],
1281       ['ASN1_STRING_get0_data'],
1282       ['HMAC_CTX_new'],
1283       ['HMAC_CTX_free'],
1285       # OpenSSL versions before 1.1.0 required setting callback functions, for
1286       # thread-safety. In 1.1.0, it's no longer required, and CRYPTO_lock()
1287       # function was removed.
1288       ['CRYPTO_lock'],
1290       # Function introduced in OpenSSL 1.1.1
1291       ['X509_get_signature_info'],
1292     ]
1294     are_openssl_funcs_complete = true
1295     foreach c : check_funcs
1296       func = c.get(0)
1297       val = cc.has_function(func, args: test_c_args, dependencies: ssl_int)
1298       required = c.get(1, {}).get('required', false)
1299       if required and not val
1300         are_openssl_funcs_complete = false
1301         if openssl_required
1302           error('openssl function @0@ is required'.format(func))
1303         endif
1304         break
1305       elif not required
1306         cdata.set('HAVE_' + func.to_upper(), val ? 1 : false)
1307       endif
1308     endforeach
1310     if are_openssl_funcs_complete
1311       cdata.set('USE_OPENSSL', 1,
1312                 description: 'Define to 1 to build with OpenSSL support. (-Dssl=openssl)')
1313       cdata.set('OPENSSL_API_COMPAT', '0x10001000L',
1314                 description: 'Define to the OpenSSL API version in use. This avoids deprecation warnings from newer OpenSSL versions.')
1315       ssl_library = 'openssl'
1316     else
1317       ssl = not_found_dep
1318     endif
1319   endif
1320 endif
1322 if sslopt == 'auto' and auto_features.enabled() and not ssl.found()
1323   error('no SSL library found')
1324 endif
1328 ###############################################################
1329 # Library: uuid
1330 ###############################################################
1332 uuidopt = get_option('uuid')
1333 if uuidopt != 'none'
1334   uuidname = uuidopt.to_upper()
1335   if uuidopt == 'e2fs'
1336     uuid = dependency('uuid', required: true)
1337     uuidfunc = 'uuid_generate'
1338     uuidheader = 'uuid/uuid.h'
1339   elif uuidopt == 'bsd'
1340     # libc should have uuid function
1341     uuid = declare_dependency()
1342     uuidfunc = 'uuid_to_string'
1343     uuidheader = 'uuid.h'
1344   elif uuidopt == 'ossp'
1345     uuid = dependency('ossp-uuid', required: true)
1346     uuidfunc = 'uuid_export'
1347     uuidheader = 'uuid.h'
1348   else
1349     error('unknown uuid build option value: @0@'.format(uuidopt))
1350   endif
1352   if not cc.has_header_symbol(uuidheader, uuidfunc, args: test_c_args, dependencies: uuid)
1353     error('uuid library @0@ missing required function @1@'.format(uuidopt, uuidfunc))
1354   endif
1355   cdata.set('HAVE_@0@'.format(uuidheader.underscorify().to_upper()), 1)
1357   cdata.set('HAVE_UUID_@0@'.format(uuidname), 1,
1358            description: 'Define to 1 if you have @0@ UUID support.'.format(uuidname))
1359 else
1360   uuid = not_found_dep
1361 endif
1365 ###############################################################
1366 # Library: zlib
1367 ###############################################################
1369 zlibopt = get_option('zlib')
1370 zlib = not_found_dep
1371 if not zlibopt.disabled()
1372   zlib_t = dependency('zlib', required: zlibopt)
1374   if zlib_t.type_name() == 'internal'
1375     # if fallback was used, we don't need to test if headers are present (they
1376     # aren't built yet, so we can't test)
1377     zlib = zlib_t
1378   elif not zlib_t.found()
1379     warning('did not find zlib')
1380   elif not cc.has_header('zlib.h',
1381       args: test_c_args, include_directories: postgres_inc,
1382       dependencies: [zlib_t], required: zlibopt.enabled())
1383     warning('zlib header not found')
1384   elif not cc.has_type('z_streamp',
1385       dependencies: [zlib_t], prefix: '#include <zlib.h>',
1386       args: test_c_args, include_directories: postgres_inc)
1387     if zlibopt.enabled()
1388       error('zlib version is too old')
1389     else
1390       warning('zlib version is too old')
1391     endif
1392   else
1393     zlib = zlib_t
1394   endif
1396   if zlib.found()
1397     cdata.set('HAVE_LIBZ', 1)
1398   endif
1399 endif
1403 ###############################################################
1404 # Library: tap test dependencies
1405 ###############################################################
1407 # Check whether tap tests are enabled or not
1408 tap_tests_enabled = false
1409 tapopt = get_option('tap_tests')
1410 if not tapopt.disabled()
1411   # Checking for perl modules for tap tests
1412   perl_ipc_run_check = run_command(perl, 'config/check_modules.pl', check: false)
1413   if perl_ipc_run_check.returncode() != 0
1414     message(perl_ipc_run_check.stderr().strip())
1415     if tapopt.enabled()
1416       error('Additional Perl modules are required to run TAP tests.')
1417     else
1418       warning('Additional Perl modules are required to run TAP tests.')
1419     endif
1420   else
1421     tap_tests_enabled = true
1422   endif
1423 endif
1427 ###############################################################
1428 # Library: zstd
1429 ###############################################################
1431 zstdopt = get_option('zstd')
1432 if not zstdopt.disabled()
1433   zstd = dependency('libzstd', required: zstdopt, version: '>=1.4.0')
1435   if zstd.found()
1436     cdata.set('USE_ZSTD', 1)
1437     cdata.set('HAVE_LIBZSTD', 1)
1438   endif
1440 else
1441   zstd = not_found_dep
1442 endif
1446 ###############################################################
1447 # Compiler tests
1448 ###############################################################
1450 # Do we need -std=c99 to compile C99 code? We don't want to add -std=c99
1451 # unnecessarily, because we optionally rely on newer features.
1452 c99_test = '''
1453 #include <stdbool.h>
1454 #include <complex.h>
1455 #include <tgmath.h>
1456 #include <inttypes.h>
1458 struct named_init_test {
1459   int a;
1460   int b;
1463 extern void structfunc(struct named_init_test);
1465 int main(int argc, char **argv)
1467   struct named_init_test nit = {
1468     .a = 3,
1469     .b = 5,
1470   };
1472   for (int loop_var = 0; loop_var < 3; loop_var++)
1473   {
1474     nit.a += nit.b;
1475   }
1477   structfunc((struct named_init_test){1, 0});
1479   return nit.a != 0;
1483 if not cc.compiles(c99_test, name: 'c99', args: test_c_args)
1484   if cc.compiles(c99_test, name: 'c99 with -std=c99',
1485         args: test_c_args + ['-std=c99'])
1486     test_c_args += '-std=c99'
1487     cflags += '-std=c99'
1488   else
1489     error('C compiler does not support C99')
1490   endif
1491 endif
1493 sizeof_long = cc.sizeof('long', args: test_c_args)
1494 cdata.set('SIZEOF_LONG', sizeof_long)
1495 if sizeof_long == 8
1496   cdata.set('HAVE_LONG_INT_64', 1)
1497   cdata.set('PG_INT64_TYPE', 'long int')
1498   cdata.set_quoted('INT64_MODIFIER', 'l')
1499 elif sizeof_long == 4 and cc.sizeof('long long', args: test_c_args) == 8
1500   cdata.set('HAVE_LONG_LONG_INT_64', 1)
1501   cdata.set('PG_INT64_TYPE', 'long long int')
1502   cdata.set_quoted('INT64_MODIFIER', 'll')
1503 else
1504   error('do not know how to get a 64bit int')
1505 endif
1507 if host_machine.endian() == 'big'
1508   cdata.set('WORDS_BIGENDIAN', 1)
1509 endif
1511 alignof_types = ['short', 'int', 'long', 'double']
1512 maxalign = 0
1513 foreach t : alignof_types
1514   align = cc.alignment(t, args: test_c_args)
1515   if maxalign < align
1516     maxalign = align
1517   endif
1518   cdata.set('ALIGNOF_@0@'.format(t.to_upper()), align)
1519 endforeach
1520 cdata.set('MAXIMUM_ALIGNOF', maxalign)
1522 cdata.set('SIZEOF_VOID_P', cc.sizeof('void *', args: test_c_args))
1523 cdata.set('SIZEOF_SIZE_T', cc.sizeof('size_t', args: test_c_args))
1526 # Check if __int128 is a working 128 bit integer type, and if so
1527 # define PG_INT128_TYPE to that typename.
1529 # This currently only detects a GCC/clang extension, but support for other
1530 # environments may be added in the future.
1532 # For the moment we only test for support for 128bit math; support for
1533 # 128bit literals and snprintf is not required.
1534 if cc.links('''
1535   /*
1536    * We don't actually run this test, just link it to verify that any support
1537    * functions needed for __int128 are present.
1538    *
1539    * These are globals to discourage the compiler from folding all the
1540    * arithmetic tests down to compile-time constants.  We do not have
1541    * convenient support for 128bit literals at this point...
1542    */
1543   __int128 a = 48828125;
1544   __int128 b = 97656250;
1546   int main(void)
1547   {
1548       __int128 c,d;
1549       a = (a << 12) + 1; /* 200000000001 */
1550       b = (b << 12) + 5; /* 400000000005 */
1551       /* try the most relevant arithmetic ops */
1552       c = a * b;
1553       d = (c + b) / b;
1554       /* must use the results, else compiler may optimize arithmetic away */
1555       return d != a+1;
1556   }''',
1557   name: '__int128',
1558   args: test_c_args)
1560   buggy_int128 = false
1562   # Use of non-default alignment with __int128 tickles bugs in some compilers.
1563   # If not cross-compiling, we can test for bugs and disable use of __int128
1564   # with buggy compilers.  If cross-compiling, hope for the best.
1565   # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83925
1566   if not meson.is_cross_build()
1567     r = cc.run('''
1568     /* This must match the corresponding code in c.h: */
1569     #if defined(__GNUC__) || defined(__SUNPRO_C) || defined(__IBMC__)
1570     #define pg_attribute_aligned(a) __attribute__((aligned(a)))
1571     #elif defined(_MSC_VER)
1572     #define pg_attribute_aligned(a) __declspec(align(a))
1573     #endif
1574     typedef __int128 int128a
1575     #if defined(pg_attribute_aligned)
1576     pg_attribute_aligned(8)
1577     #endif
1578     ;
1580     int128a holder;
1581     void pass_by_val(void *buffer, int128a par) { holder = par; }
1583     int main(void)
1584     {
1585         long int i64 = 97656225L << 12;
1586         int128a q;
1587         pass_by_val(main, (int128a) i64);
1588         q = (int128a) i64;
1589         return q != holder;
1590     }''',
1591     name: '__int128 alignment bug',
1592     args: test_c_args)
1593     assert(r.compiled())
1594     if r.returncode() != 0
1595       buggy_int128 = true
1596       message('__int128 support present but buggy and thus disabled')
1597     endif
1598   endif
1600   if not buggy_int128
1601     cdata.set('PG_INT128_TYPE', '__int128')
1602     cdata.set('ALIGNOF_PG_INT128_TYPE', cc.alignment('__int128', args: test_c_args))
1603   endif
1604 endif
1607 # Check if the C compiler knows computed gotos (gcc extension, also
1608 # available in at least clang).  If so, define HAVE_COMPUTED_GOTO.
1610 # Checking whether computed gotos are supported syntax-wise ought to
1611 # be enough, as the syntax is otherwise illegal.
1612 if cc.compiles('''
1613     static inline int foo(void)
1614     {
1615       void *labeladdrs[] = {&&my_label};
1616       goto *labeladdrs[0];
1617       my_label:
1618       return 1;
1619     }''',
1620     args: test_c_args)
1621   cdata.set('HAVE_COMPUTED_GOTO', 1)
1622 endif
1625 # Check if the C compiler understands _Static_assert(),
1626 # and define HAVE__STATIC_ASSERT if so.
1628 # We actually check the syntax ({ _Static_assert(...) }), because we need
1629 # gcc-style compound expressions to be able to wrap the thing into macros.
1630 if cc.compiles('''
1631     int main(int arg, char **argv)
1632     {
1633         ({ _Static_assert(1, "foo"); });
1634     }
1635     ''',
1636     args: test_c_args)
1637   cdata.set('HAVE__STATIC_ASSERT', 1)
1638 endif
1641 # We use <stdbool.h> if we have it and it declares type bool as having
1642 # size 1.  Otherwise, c.h will fall back to declaring bool as unsigned char.
1643 if cc.has_type('_Bool', args: test_c_args) \
1644     and cc.has_type('bool', prefix: '#include <stdbool.h>', args: test_c_args) \
1645     and cc.sizeof('bool', prefix: '#include <stdbool.h>', args: test_c_args) == 1
1646   cdata.set('HAVE__BOOL', 1)
1647   cdata.set('PG_USE_STDBOOL', 1)
1648 endif
1651 # Need to check a call with %m because netbsd supports gnu_printf but emits a
1652 # warning for each use of %m.
1653 printf_attributes = ['gnu_printf', '__syslog__', 'printf']
1654 testsrc = '''
1655 extern void emit_log(int ignore, const char *fmt,...) __attribute__((format(@0@, 2,3)));
1656 static void call_log(void)
1658     emit_log(0, "error: %s: %m", "foo");
1661 attrib_error_args = cc.get_supported_arguments('-Werror=format', '-Werror=ignored-attributes')
1662 foreach a : printf_attributes
1663   if cc.compiles(testsrc.format(a),
1664       args: test_c_args + attrib_error_args, name: 'format ' + a)
1665     cdata.set('PG_PRINTF_ATTRIBUTE', a)
1666     break
1667   endif
1668 endforeach
1671 if cc.has_function_attribute('visibility:default') and \
1672     cc.has_function_attribute('visibility:hidden')
1673   cdata.set('HAVE_VISIBILITY_ATTRIBUTE', 1)
1675   # Only newer versions of meson know not to apply gnu_symbol_visibility =
1676   # inlineshidden to C code as well... And either way, we want to put these
1677   # flags into exported files (pgxs, .pc files).
1678   cflags_mod += '-fvisibility=hidden'
1679   cxxflags_mod += ['-fvisibility=hidden', '-fvisibility-inlines-hidden']
1680   ldflags_mod += '-fvisibility=hidden'
1681 endif
1684 # Check if various builtins exist. Some builtins are tested separately,
1685 # because we want to test something more complicated than the generic case.
1686 builtins = [
1687   'bswap16',
1688   'bswap32',
1689   'bswap64',
1690   'clz',
1691   'ctz',
1692   'constant_p',
1693   'frame_address',
1694   'popcount',
1695   'unreachable',
1698 foreach builtin : builtins
1699   fname = '__builtin_@0@'.format(builtin)
1700   if cc.has_function(fname, args: test_c_args)
1701     cdata.set('HAVE@0@'.format(fname.to_upper()), 1)
1702   endif
1703 endforeach
1706 # Check if the C compiler understands __builtin_types_compatible_p,
1707 # and define HAVE__BUILTIN_TYPES_COMPATIBLE_P if so.
1709 # We check usage with __typeof__, though it's unlikely any compiler would
1710 # have the former and not the latter.
1711 if cc.compiles('''
1712     static int x;
1713     static int y[__builtin_types_compatible_p(__typeof__(x), int)];
1714     ''',
1715     name: '__builtin_types_compatible_p',
1716     args: test_c_args)
1717   cdata.set('HAVE__BUILTIN_TYPES_COMPATIBLE_P', 1)
1718 endif
1721 # Check if the C compiler understands __builtin_$op_overflow(),
1722 # and define HAVE__BUILTIN_OP_OVERFLOW if so.
1724 # Check for the most complicated case, 64 bit multiplication, as a
1725 # proxy for all of the operations.  To detect the case where the compiler
1726 # knows the function but library support is missing, we must link not just
1727 # compile, and store the results in global variables so the compiler doesn't
1728 # optimize away the call.
1729 if cc.links('''
1730     INT64 a = 1;
1731     INT64 b = 1;
1732     INT64 result;
1734     int main(void)
1735     {
1736         return __builtin_mul_overflow(a, b, &result);
1737     }''',
1738     name: '__builtin_mul_overflow',
1739     args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))],
1740     )
1741   cdata.set('HAVE__BUILTIN_OP_OVERFLOW', 1)
1742 endif
1745 # XXX: The configure.ac check for __cpuid() is broken, we don't copy that
1746 # here. To prevent problems due to two detection methods working, stop
1747 # checking after one.
1748 if cc.links('''
1749     #include <cpuid.h>
1750     int main(int arg, char **argv)
1751     {
1752         unsigned int exx[4] = {0, 0, 0, 0};
1753         __get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
1754     }
1755     ''', name: '__get_cpuid',
1756     args: test_c_args)
1757   cdata.set('HAVE__GET_CPUID', 1)
1758 elif cc.links('''
1759     #include <intrin.h>
1760     int main(int arg, char **argv)
1761     {
1762         unsigned int exx[4] = {0, 0, 0, 0};
1763         __cpuid(exx, 1);
1764     }
1765     ''', name: '__cpuid',
1766     args: test_c_args)
1767   cdata.set('HAVE__CPUID', 1)
1768 endif
1771 # Defend against clang being used on x86-32 without SSE2 enabled.  As current
1772 # versions of clang do not understand -fexcess-precision=standard, the use of
1773 # x87 floating point operations leads to problems like isinf possibly returning
1774 # false for a value that is infinite when converted from the 80bit register to
1775 # the 8byte memory representation.
1777 # Only perform the test if the compiler doesn't understand
1778 # -fexcess-precision=standard, that way a potentially fixed compiler will work
1779 # automatically.
1780 if '-fexcess-precision=standard' not in cflags
1781   if not cc.compiles('''
1782 #if defined(__clang__) && defined(__i386__) && !defined(__SSE2_MATH__)
1783 choke me
1784 #endif''',
1785       name: '', args: test_c_args)
1786     error('Compiling PostgreSQL with clang, on 32bit x86, requires SSE2 support. Use -msse2 or use gcc.')
1787   endif
1788 endif
1792 ###############################################################
1793 # Compiler flags
1794 ###############################################################
1796 common_functional_flags = [
1797   # Disable strict-aliasing rules; needed for gcc 3.3+
1798   '-fno-strict-aliasing',
1799   # Disable optimizations that assume no overflow; needed for gcc 4.3+
1800   '-fwrapv',
1801   '-fexcess-precision=standard',
1804 cflags += cc.get_supported_arguments(common_functional_flags)
1805 if llvm.found()
1806   cxxflags += cpp.get_supported_arguments(common_functional_flags)
1807 endif
1809 vectorize_cflags = cc.get_supported_arguments(['-ftree-vectorize'])
1810 unroll_loops_cflags = cc.get_supported_arguments(['-funroll-loops'])
1812 common_warning_flags = [
1813   '-Wmissing-prototypes',
1814   '-Wpointer-arith',
1815   # Really don't want VLAs to be used in our dialect of C
1816   '-Werror=vla',
1817   # On macOS, complain about usage of symbols newer than the deployment target
1818   '-Werror=unguarded-availability-new',
1819   '-Wendif-labels',
1820   '-Wmissing-format-attribute',
1821   '-Wimplicit-fallthrough=3',
1822   '-Wcast-function-type',
1823   '-Wshadow=compatible-local',
1824   # This was included in -Wall/-Wformat in older GCC versions
1825   '-Wformat-security',
1828 cflags_warn += cc.get_supported_arguments(common_warning_flags)
1829 if llvm.found()
1830   cxxflags_warn += cpp.get_supported_arguments(common_warning_flags)
1831 endif
1833 # A few places with imported code get a pass on -Wdeclaration-after-statement, remember
1834 # the result for them
1835 cflags_no_decl_after_statement = []
1836 if cc.has_argument('-Wdeclaration-after-statement')
1837   cflags_warn += '-Wdeclaration-after-statement'
1838   cflags_no_decl_after_statement += '-Wno-declaration-after-statement'
1839 endif
1842 # The following tests want to suppress various unhelpful warnings by adding
1843 # -Wno-foo switches.  But gcc won't complain about unrecognized -Wno-foo
1844 # switches, so we have to test for the positive form and if that works,
1845 # add the negative form.
1847 negative_warning_flags = [
1848   # Suppress clang's unhelpful unused-command-line-argument warnings.
1849   'unused-command-line-argument',
1851   # Remove clang 12+'s compound-token-split-by-macro, as this causes a lot
1852   # of warnings when building plperl because of usages in the Perl headers.
1853   'compound-token-split-by-macro',
1855   # Similarly disable useless truncation warnings from gcc 8+
1856   'format-truncation',
1857   'stringop-truncation',
1859   # Suppress clang 16's strict warnings about function casts
1860   'cast-function-type-strict',
1862   # To make warning_level=2 / -Wextra work, we'd need at least the following
1863   # 'clobbered',
1864   # 'missing-field-initializers',
1865   # 'sign-compare',
1866   # 'unused-parameter',
1869 foreach w : negative_warning_flags
1870   if cc.has_argument('-W' + w)
1871     cflags_warn += '-Wno-' + w
1872   endif
1873   if llvm.found() and cpp.has_argument('-W' + w)
1874     cxxflags_warn += '-Wno-' + w
1875   endif
1876 endforeach
1879 # From Project.pm
1880 if cc.get_id() == 'msvc'
1881   cflags_warn += [
1882     '/wd4018', # signed/unsigned mismatch
1883     '/wd4244', # conversion from 'type1' to 'type2', possible loss of data
1884     '/wd4273', # inconsistent DLL linkage
1885     '/wd4101', # unreferenced local variable
1886     '/wd4102', # unreferenced label
1887     '/wd4090', # different 'modifier' qualifiers
1888     '/wd4267', # conversion from 'size_t' to 'type', possible loss of data
1889   ]
1891   cppflags += [
1892     '/DWIN32',
1893     '/DWINDOWS',
1894     '/D__WINDOWS__',
1895     '/D__WIN32__',
1896     '/D_CRT_SECURE_NO_DEPRECATE',
1897     '/D_CRT_NONSTDC_NO_DEPRECATE',
1898   ]
1900   # We never need export libraries. As link.exe reports their creation, they
1901   # are unnecessarily noisy. Similarly, we don't need import library for
1902   # modules, we only import them dynamically, and they're also noisy.
1903   ldflags += '/NOEXP'
1904   ldflags_mod += '/NOIMPLIB'
1905 endif
1909 ###############################################################
1910 # Atomics
1911 ###############################################################
1913 if not get_option('spinlocks')
1914   warning('Not using spinlocks will cause poor performance')
1915 else
1916   cdata.set('HAVE_SPINLOCKS', 1)
1917 endif
1919 if not get_option('atomics')
1920   warning('Not using atomics will cause poor performance')
1921 else
1922   # XXX: perhaps we should require some atomics support in this case these
1923   # days?
1924   cdata.set('HAVE_ATOMICS', 1)
1926   atomic_checks = [
1927     {'name': 'HAVE_GCC__SYNC_CHAR_TAS',
1928      'desc': '__sync_lock_test_and_set(char)',
1929      'test': '''
1930 char lock = 0;
1931 __sync_lock_test_and_set(&lock, 1);
1932 __sync_lock_release(&lock);'''},
1934     {'name': 'HAVE_GCC__SYNC_INT32_TAS',
1935      'desc': '__sync_lock_test_and_set(int32)',
1936      'test': '''
1937 int lock = 0;
1938 __sync_lock_test_and_set(&lock, 1);
1939 __sync_lock_release(&lock);'''},
1941     {'name': 'HAVE_GCC__SYNC_INT32_CAS',
1942      'desc': '__sync_val_compare_and_swap(int32)',
1943      'test': '''
1944 int val = 0;
1945 __sync_val_compare_and_swap(&val, 0, 37);'''},
1947     {'name': 'HAVE_GCC__SYNC_INT64_CAS',
1948      'desc': '__sync_val_compare_and_swap(int64)',
1949      'test': '''
1950 INT64 val = 0;
1951 __sync_val_compare_and_swap(&val, 0, 37);'''},
1953     {'name': 'HAVE_GCC__ATOMIC_INT32_CAS',
1954      'desc': ' __atomic_compare_exchange_n(int32)',
1955      'test': '''
1956 int val = 0;
1957 int expect = 0;
1958 __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);'''},
1960     {'name': 'HAVE_GCC__ATOMIC_INT64_CAS',
1961      'desc': ' __atomic_compare_exchange_n(int64)',
1962      'test': '''
1963 INT64 val = 0;
1964 INT64 expect = 0;
1965 __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);'''},
1966   ]
1968   foreach check : atomic_checks
1969     test = '''
1970 int main(void)
1973 }'''.format(check['test'])
1975     cdata.set(check['name'],
1976       cc.links(test,
1977         name: check['desc'],
1978         args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))]) ? 1 : false
1979     )
1980   endforeach
1982 endif
1986 ###############################################################
1987 # Select CRC-32C implementation.
1989 # If we are targeting a processor that has Intel SSE 4.2 instructions, we can
1990 # use the special CRC instructions for calculating CRC-32C. If we're not
1991 # targeting such a processor, but we can nevertheless produce code that uses
1992 # the SSE intrinsics, perhaps with some extra CFLAGS, compile both
1993 # implementations and select which one to use at runtime, depending on whether
1994 # SSE 4.2 is supported by the processor we're running on.
1996 # Similarly, if we are targeting an ARM processor that has the CRC
1997 # instructions that are part of the ARMv8 CRC Extension, use them. And if
1998 # we're not targeting such a processor, but can nevertheless produce code that
1999 # uses the CRC instructions, compile both, and select at runtime.
2000 ###############################################################
2002 have_optimized_crc = false
2003 cflags_crc = []
2004 if host_cpu == 'x86' or host_cpu == 'x86_64'
2006   if cc.get_id() == 'msvc'
2007     cdata.set('USE_SSE42_CRC32C', false)
2008     cdata.set('USE_SSE42_CRC32C_WITH_RUNTIME_CHECK', 1)
2009     have_optimized_crc = true
2010   else
2012     prog = '''
2013 #include <nmmintrin.h>
2015 int main(void)
2017     unsigned int crc = 0;
2018     crc = _mm_crc32_u8(crc, 0);
2019     crc = _mm_crc32_u32(crc, 0);
2020     /* return computed value, to prevent the above being optimized away */
2021     return crc == 0;
2025     if cc.links(prog, name: '_mm_crc32_u8 and _mm_crc32_u32 without -msse4.2',
2026           args: test_c_args)
2027       # Use Intel SSE 4.2 unconditionally.
2028       cdata.set('USE_SSE42_CRC32C', 1)
2029       have_optimized_crc = true
2030     elif cc.links(prog, name: '_mm_crc32_u8 and _mm_crc32_u32 with -msse4.2',
2031           args: test_c_args + ['-msse4.2'])
2032       # Use Intel SSE 4.2, with runtime check. The CPUID instruction is needed for
2033       # the runtime check.
2034       cflags_crc += '-msse4.2'
2035       cdata.set('USE_SSE42_CRC32C', false)
2036       cdata.set('USE_SSE42_CRC32C_WITH_RUNTIME_CHECK', 1)
2037       have_optimized_crc = true
2038     endif
2040   endif
2042 elif host_cpu == 'arm' or host_cpu == 'aarch64'
2044   prog = '''
2045 #include <arm_acle.h>
2047 int main(void)
2049     unsigned int crc = 0;
2050     crc = __crc32cb(crc, 0);
2051     crc = __crc32ch(crc, 0);
2052     crc = __crc32cw(crc, 0);
2053     crc = __crc32cd(crc, 0);
2055     /* return computed value, to prevent the above being optimized away */
2056     return crc == 0;
2060   if cc.links(prog, name: '__crc32cb, __crc32ch, __crc32cw, and __crc32cd without -march=armv8-a+crc',
2061       args: test_c_args)
2062     # Use ARM CRC Extension unconditionally
2063     cdata.set('USE_ARMV8_CRC32C', 1)
2064     have_optimized_crc = true
2065   elif cc.links(prog, name: '__crc32cb, __crc32ch, __crc32cw, and __crc32cd with -march=armv8-a+crc',
2066       args: test_c_args + ['-march=armv8-a+crc'])
2067     # Use ARM CRC Extension, with runtime check
2068     cflags_crc += '-march=armv8-a+crc'
2069     cdata.set('USE_ARMV8_CRC32C', false)
2070     cdata.set('USE_ARMV8_CRC32C_WITH_RUNTIME_CHECK', 1)
2071     have_optimized_crc = true
2072   endif
2073 endif
2075 if not have_optimized_crc
2076   # fall back to slicing-by-8 algorithm, which doesn't require any special CPU
2077   # support.
2078   cdata.set('USE_SLICING_BY_8_CRC32C', 1)
2079 endif
2083 ###############################################################
2084 # Other CPU specific stuff
2085 ###############################################################
2087 if host_cpu == 'x86_64'
2089   if cc.compiles('''
2090       void main(void)
2091       {
2092           long long x = 1; long long r;
2093           __asm__ __volatile__ (" popcntq %1,%0\n" : "=q"(r) : "rm"(x));
2094       }''',
2095       name: '@0@: popcntq instruction'.format(host_cpu),
2096       args: test_c_args)
2097     cdata.set('HAVE_X86_64_POPCNTQ', 1)
2098   endif
2100 elif host_cpu == 'ppc' or host_cpu == 'ppc64'
2101   # Check if compiler accepts "i"(x) when __builtin_constant_p(x).
2102   if cdata.has('HAVE__BUILTIN_CONSTANT_P')
2103     if cc.compiles('''
2104       static inline int
2105       addi(int ra, int si)
2106       {
2107           int res = 0;
2108           if (__builtin_constant_p(si))
2109               __asm__ __volatile__(
2110                   " addi %0,%1,%2\n" : "=r"(res) : "b"(ra), "i"(si));
2111           return res;
2112       }
2113       int test_adds(int x) { return addi(3, x) + addi(x, 5); }
2114       ''',
2115       args: test_c_args)
2116       cdata.set('HAVE_I_CONSTRAINT__BUILTIN_CONSTANT_P', 1)
2117     endif
2118   endif
2119 endif
2123 ###############################################################
2124 # Library / OS tests
2125 ###############################################################
2127 # XXX: Might be worth conditioning some checks on the OS, to avoid doing
2128 # unnecessary checks over and over, particularly on windows.
2129 header_checks = [
2130   'atomic.h',
2131   'copyfile.h',
2132   'crtdefs.h',
2133   'execinfo.h',
2134   'getopt.h',
2135   'ifaddrs.h',
2136   'langinfo.h',
2137   'mbarrier.h',
2138   'stdbool.h',
2139   'strings.h',
2140   'sys/epoll.h',
2141   'sys/event.h',
2142   'sys/personality.h',
2143   'sys/prctl.h',
2144   'sys/procctl.h',
2145   'sys/signalfd.h',
2146   'sys/ucred.h',
2147   'termios.h',
2148   'ucred.h',
2151 foreach header : header_checks
2152   varname = 'HAVE_' + header.underscorify().to_upper()
2154   # Emulate autoconf behaviour of not-found->undef, found->1
2155   found = cc.has_header(header,
2156     include_directories: postgres_inc, args: test_c_args)
2157   cdata.set(varname, found ? 1 : false,
2158             description: 'Define to 1 if you have the <@0@> header file.'.format(header))
2159 endforeach
2162 decl_checks = [
2163   ['F_FULLFSYNC', 'fcntl.h'],
2164   ['fdatasync', 'unistd.h'],
2165   ['posix_fadvise', 'fcntl.h'],
2166   ['strlcat', 'string.h'],
2167   ['strlcpy', 'string.h'],
2168   ['strnlen', 'string.h'],
2171 # Need to check for function declarations for these functions, because
2172 # checking for library symbols wouldn't handle deployment target
2173 # restrictions on macOS
2174 decl_checks += [
2175   ['preadv', 'sys/uio.h'],
2176   ['pwritev', 'sys/uio.h'],
2179 foreach c : decl_checks
2180   func = c.get(0)
2181   header = c.get(1)
2182   args = c.get(2, {})
2183   varname = 'HAVE_DECL_' + func.underscorify().to_upper()
2185   found = cc.has_header_symbol(header, func,
2186     args: test_c_args, include_directories: postgres_inc,
2187     kwargs: args)
2188   cdata.set10(varname, found, description:
2189 '''Define to 1 if you have the declaration of `@0@', and to 0 if you
2190    don't.'''.format(func))
2191 endforeach
2194 if cc.has_type('struct option',
2195     args: test_c_args, include_directories: postgres_inc,
2196     prefix: '@0@'.format(cdata.get('HAVE_GETOPT_H')) == '1' ? '#include <getopt.h>' : '')
2197   cdata.set('HAVE_STRUCT_OPTION', 1)
2198 endif
2201 foreach c : ['opterr', 'optreset']
2202   varname = 'HAVE_INT_' + c.underscorify().to_upper()
2204   if cc.links('''
2205 #include <unistd.h>
2206 int main(void)
2208     extern int @0@;
2209     @0@ = 1;
2211 '''.format(c), name: c, args: test_c_args)
2212     cdata.set(varname, 1)
2213   else
2214     cdata.set(varname, false)
2215   endif
2216 endforeach
2218 if cc.has_type('socklen_t',
2219     args: test_c_args, include_directories: postgres_inc,
2220     prefix: '''
2221 #include <sys/socket.h>''')
2222   cdata.set('HAVE_SOCKLEN_T', 1)
2223 endif
2225 if cc.has_member('struct sockaddr', 'sa_len',
2226     args: test_c_args, include_directories: postgres_inc,
2227     prefix: '''
2228 #include <sys/types.h>
2229 #include <sys/socket.h>''')
2230   cdata.set('HAVE_STRUCT_SOCKADDR_SA_LEN', 1)
2231 endif
2233 if cc.has_member('struct tm', 'tm_zone',
2234     args: test_c_args, include_directories: postgres_inc,
2235     prefix: '''
2236 #include <sys/types.h>
2237 #include <time.h>
2238 ''')
2239   cdata.set('HAVE_STRUCT_TM_TM_ZONE', 1)
2240 endif
2242 if cc.compiles('''
2243 #include <time.h>
2244 extern int foo(void);
2245 int foo(void)
2247     return timezone / 60;
2249 ''',
2250     name: 'global variable `timezone\' exists',
2251     args: test_c_args, include_directories: postgres_inc)
2252   cdata.set('HAVE_INT_TIMEZONE', 1)
2253 else
2254   cdata.set('HAVE_INT_TIMEZONE', false)
2255 endif
2257 if cc.has_type('union semun',
2258     args: test_c_args,
2259     include_directories: postgres_inc,
2260     prefix: '''
2261 #include <sys/types.h>
2262 #include <sys/ipc.h>
2263 #include <sys/sem.h>
2264 ''')
2265   cdata.set('HAVE_UNION_SEMUN', 1)
2266 endif
2268 if cc.compiles('''
2269 #include <string.h>
2270 int main(void)
2272   char buf[100];
2273   switch (strerror_r(1, buf, sizeof(buf)))
2274   { case 0: break; default: break; }
2275 }''',
2276     name: 'strerror_r',
2277     args: test_c_args, include_directories: postgres_inc)
2278   cdata.set('STRERROR_R_INT', 1)
2279 else
2280   cdata.set('STRERROR_R_INT', false)
2281 endif
2283 # Check for the locale_t type and find the right header file.  macOS
2284 # needs xlocale.h; standard is locale.h, but glibc also has an
2285 # xlocale.h file that we should not use.  MSVC has a replacement
2286 # defined in src/include/port/win32_port.h.
2287 if cc.has_type('locale_t', prefix: '#include <locale.h>')
2288   cdata.set('HAVE_LOCALE_T', 1)
2289 elif cc.has_type('locale_t', prefix: '#include <xlocale.h>')
2290   cdata.set('HAVE_LOCALE_T', 1)
2291   cdata.set('LOCALE_T_IN_XLOCALE', 1)
2292 elif cc.get_id() == 'msvc'
2293   cdata.set('HAVE_LOCALE_T', 1)
2294 endif
2296 # Check if the C compiler understands typeof or a variant.  Define
2297 # HAVE_TYPEOF if so, and define 'typeof' to the actual key word.
2298 foreach kw : ['typeof', '__typeof__', 'decltype']
2299   if cc.compiles('''
2300 int main(void)
2302     int x = 0;
2303     @0@(x) y;
2304     y = x;
2305     return y;
2307 '''.format(kw),
2308     name: 'typeof()',
2309     args: test_c_args, include_directories: postgres_inc)
2311     cdata.set('HAVE_TYPEOF', 1)
2312     if kw != 'typeof'
2313       cdata.set('typeof', kw)
2314     endif
2316     break
2317   endif
2318 endforeach
2321 # Try to find a declaration for wcstombs_l().  It might be in stdlib.h
2322 # (following the POSIX requirement for wcstombs()), or in locale.h, or in
2323 # xlocale.h.  If it's in the latter, define WCSTOMBS_L_IN_XLOCALE.
2324 wcstombs_l_test = '''
2325 #include <stdlib.h>
2326 #include <locale.h>
2329 void main(void)
2331 #ifndef wcstombs_l
2332     (void) wcstombs_l;
2333 #endif
2336 if (not cc.compiles(wcstombs_l_test.format(''),
2337       name: 'wcstombs_l') and
2338     cc.compiles(wcstombs_l_test.format('#include <xlocale.h>'),
2339       name: 'wcstombs_l in xlocale.h'))
2340     cdata.set('WCSTOMBS_L_IN_XLOCALE', 1)
2341 endif
2344 # MSVC doesn't cope well with defining restrict to __restrict, the spelling it
2345 # understands, because it conflicts with __declspec(restrict). Therefore we
2346 # define pg_restrict to the appropriate definition, which presumably won't
2347 # conflict.
2349 # We assume C99 support, so we don't need to make this conditional.
2351 # XXX: Historically we allowed platforms to disable restrict in template
2352 # files, but that was only added for AIX when building with XLC, which we
2353 # don't support yet.
2354 cdata.set('pg_restrict', '__restrict')
2357 # Most libraries are included only if they demonstrably provide a function we
2358 # need, but libm is an exception: always include it, because there are too
2359 # many compilers that play cute optimization games that will break probes for
2360 # standard functions such as pow().
2361 os_deps += cc.find_library('m', required: false)
2363 rt_dep = cc.find_library('rt', required: false)
2365 dl_dep = cc.find_library('dl', required: false)
2367 util_dep = cc.find_library('util', required: false)
2368 posix4_dep = cc.find_library('posix4', required: false)
2370 getopt_dep = cc.find_library('getopt', required: false)
2371 gnugetopt_dep = cc.find_library('gnugetopt', required: false)
2372 # Check if we want to replace getopt/getopt_long even if provided by the system
2373 # - Mingw has adopted a GNU-centric interpretation of optind/optreset,
2374 #   so always use our version on Windows
2375 # - On OpenBSD and Solaris, getopt() doesn't do what we want for long options
2376 #   (i.e., allow '-' as a flag character), so use our version on those platforms
2377 # - We want to use system's getopt_long() only if the system provides struct
2378 #   option
2379 always_replace_getopt = host_system in ['windows', 'cygwin', 'openbsd', 'solaris']
2380 always_replace_getopt_long = host_system in ['windows', 'cygwin'] or not cdata.has('HAVE_STRUCT_OPTION')
2382 # Required on BSDs
2383 execinfo_dep = cc.find_library('execinfo', required: false)
2385 if host_system == 'cygwin'
2386   cygipc_dep = cc.find_library('cygipc', required: false)
2387 else
2388   cygipc_dep = not_found_dep
2389 endif
2391 if host_system == 'sunos'
2392   socket_dep = cc.find_library('socket', required: false)
2393 else
2394   socket_dep = not_found_dep
2395 endif
2397 # XXX: Might be worth conditioning some checks on the OS, to avoid doing
2398 # unnecessary checks over and over, particularly on windows.
2399 func_checks = [
2400   ['_configthreadlocale', {'skip': host_system != 'windows'}],
2401   ['backtrace_symbols', {'dependencies': [execinfo_dep]}],
2402   ['clock_gettime', {'dependencies': [rt_dep, posix4_dep], 'define': false}],
2403   ['copyfile'],
2404   # gcc/clang's sanitizer helper library provides dlopen but not dlsym, thus
2405   # when enabling asan the dlopen check doesn't notice that -ldl is actually
2406   # required. Just checking for dlsym() ought to suffice.
2407   ['dlsym', {'dependencies': [dl_dep], 'define': false}],
2408   ['explicit_bzero'],
2409   ['fdatasync', {'dependencies': [rt_dep, posix4_dep], 'define': false}], # Solaris
2410   ['getifaddrs'],
2411   ['getopt', {'dependencies': [getopt_dep, gnugetopt_dep], 'skip': always_replace_getopt}],
2412   ['getopt_long', {'dependencies': [getopt_dep, gnugetopt_dep], 'skip': always_replace_getopt_long}],
2413   ['getpeereid'],
2414   ['getpeerucred'],
2415   ['inet_aton'],
2416   ['inet_pton'],
2417   ['kqueue'],
2418   ['mbstowcs_l'],
2419   ['memset_s'],
2420   ['mkdtemp'],
2421   ['posix_fadvise'],
2422   ['posix_fallocate'],
2423   ['ppoll'],
2424   ['pstat'],
2425   ['pthread_barrier_wait', {'dependencies': [thread_dep]}],
2426   ['pthread_is_threaded_np', {'dependencies': [thread_dep]}],
2427   ['sem_init', {'dependencies': [rt_dep, thread_dep], 'skip': sema_kind != 'unnamed_posix', 'define': false}],
2428   ['setproctitle', {'dependencies': [util_dep]}],
2429   ['setproctitle_fast'],
2430   ['shm_open', {'dependencies': [rt_dep], 'define': false}],
2431   ['shm_unlink', {'dependencies': [rt_dep], 'define': false}],
2432   ['shmget', {'dependencies': [cygipc_dep], 'define': false}],
2433   ['socket', {'dependencies': [socket_dep], 'define': false}],
2434   ['strchrnul'],
2435   ['strerror_r', {'dependencies': [thread_dep]}],
2436   ['strlcat'],
2437   ['strlcpy'],
2438   ['strnlen'],
2439   ['strsignal'],
2440   ['sync_file_range'],
2441   ['syncfs'],
2442   ['uselocale'],
2443   ['wcstombs_l'],
2446 func_check_results = {}
2447 foreach c : func_checks
2448   func = c.get(0)
2449   kwargs = c.get(1, {})
2450   deps = kwargs.get('dependencies', [])
2452   if kwargs.get('skip', false)
2453     continue
2454   endif
2456   found = cc.has_function(func, args: test_c_args)
2458   if not found
2459     foreach dep : deps
2460       if not dep.found()
2461         continue
2462       endif
2463       found = cc.has_function(func, args: test_c_args,
2464                               dependencies: [dep])
2465       if found
2466         os_deps += dep
2467         break
2468       endif
2469     endforeach
2470   endif
2472   func_check_results += {func: found}
2474   if kwargs.get('define', true)
2475     # Emulate autoconf behaviour of not-found->undef, found->1
2476     cdata.set('HAVE_' + func.underscorify().to_upper(),
2477               found  ? 1 : false,
2478               description: 'Define to 1 if you have the `@0@\' function.'.format(func))
2479   endif
2480 endforeach
2483 if cc.has_function('syslog', args: test_c_args) and \
2484     cc.check_header('syslog.h', args: test_c_args)
2485   cdata.set('HAVE_SYSLOG', 1)
2486 endif
2489 # MSVC has replacements defined in src/include/port/win32_port.h.
2490 if cc.get_id() == 'msvc'
2491   cdata.set('HAVE_WCSTOMBS_L', 1)
2492   cdata.set('HAVE_MBSTOWCS_L', 1)
2493 endif
2496 # if prerequisites for unnamed posix semas aren't fulfilled, fall back to sysv
2497 # semaphores
2498 if sema_kind == 'unnamed_posix' and \
2499    not func_check_results.get('sem_init', false)
2500   sema_kind = 'sysv'
2501 endif
2503 cdata.set('USE_@0@_SHARED_MEMORY'.format(shmem_kind.to_upper()), 1)
2504 cdata.set('USE_@0@_SEMAPHORES'.format(sema_kind.to_upper()), 1)
2506 cdata.set('MEMSET_LOOP_LIMIT', memset_loop_limit)
2507 cdata.set_quoted('DLSUFFIX', dlsuffix)
2510 # built later than the rest of the version metadata, we need SIZEOF_VOID_P
2511 cdata.set_quoted('PG_VERSION_STR',
2512   'PostgreSQL @0@ on @1@-@2@, compiled by @3@-@4@, @5@-bit'.format(
2513     pg_version, host_machine.cpu_family(), host_system,
2514     cc.get_id(), cc.version(), cdata.get('SIZEOF_VOID_P') * 8,
2515   )
2520 ###############################################################
2521 # Threading
2522 ###############################################################
2524 # XXX: About to rely on thread safety in the autoconf build, so not worth
2525 # implementing a fallback.
2526 cdata.set('ENABLE_THREAD_SAFETY', 1)
2530 ###############################################################
2531 # NLS / Gettext
2532 ###############################################################
2534 nlsopt = get_option('nls')
2535 libintl = not_found_dep
2537 if not nlsopt.disabled()
2538   # otherwise there'd be lots of
2539   # "Gettext not found, all translation (po) targets will be ignored."
2540   # warnings if not found.
2541   msgfmt = find_program('msgfmt', required: nlsopt.enabled(), native: true)
2543   # meson 0.59 has this wrapped in dependency('intl')
2544   if (msgfmt.found() and
2545       cc.check_header('libintl.h', required: nlsopt,
2546         args: test_c_args, include_directories: postgres_inc))
2548     # in libc
2549     if cc.has_function('ngettext')
2550       libintl = declare_dependency()
2551     else
2552       libintl = cc.find_library('intl',
2553         has_headers: ['libintl.h'], required: nlsopt,
2554         header_include_directories: postgres_inc,
2555         dirs: test_lib_d)
2556     endif
2557   endif
2559   if libintl.found()
2560     i18n = import('i18n')
2561     cdata.set('ENABLE_NLS', 1)
2562   endif
2563 endif
2567 ###############################################################
2568 # Build
2569 ###############################################################
2571 # Set up compiler / linker arguments to be used everywhere, individual targets
2572 # can add further args directly, or indirectly via dependencies
2573 add_project_arguments(cflags, language: ['c'])
2574 add_project_arguments(cppflags, language: ['c'])
2575 add_project_arguments(cflags_warn, language: ['c'])
2576 add_project_arguments(cxxflags, language: ['cpp'])
2577 add_project_arguments(cppflags, language: ['cpp'])
2578 add_project_arguments(cxxflags_warn, language: ['cpp'])
2579 add_project_link_arguments(ldflags, language: ['c', 'cpp'])
2582 # Collect a number of lists of things while recursing through the source
2583 # tree. Later steps then can use those.
2585 # list of targets for various alias targets
2586 backend_targets = []
2587 bin_targets = []
2588 pl_targets = []
2589 contrib_targets = []
2590 testprep_targets = []
2591 nls_targets = []
2594 # Define the tests to distribute them to the correct test styles later
2595 test_deps = []
2596 tests = []
2599 # Default options for targets
2601 # First identify rpaths
2602 bin_install_rpaths = []
2603 lib_install_rpaths = []
2604 mod_install_rpaths = []
2607 # Don't add rpaths on darwin for now - as long as only absolute references to
2608 # libraries are needed, absolute LC_ID_DYLIB ensures libraries can be found in
2609 # their final destination.
2610 if host_system != 'darwin'
2611   # Add absolute path to libdir to rpath. This ensures installed binaries /
2612   # libraries find our libraries (mainly libpq).
2613   bin_install_rpaths += dir_prefix / dir_lib
2614   lib_install_rpaths += dir_prefix / dir_lib
2615   mod_install_rpaths += dir_prefix / dir_lib
2617   # Add extra_lib_dirs to rpath. This ensures we find libraries we depend on.
2618   #
2619   # Not needed on darwin even if we use relative rpaths for our own libraries,
2620   # as the install_name of libraries in extra_lib_dirs will point to their
2621   # location anyway.
2622   bin_install_rpaths += postgres_lib_d
2623   lib_install_rpaths += postgres_lib_d
2624   mod_install_rpaths += postgres_lib_d
2625 endif
2628 # Define arguments for default targets
2630 default_target_args = {
2631   'implicit_include_directories': false,
2632   'install': true,
2635 default_lib_args = default_target_args + {
2636   'name_prefix': '',
2639 internal_lib_args = default_lib_args + {
2640   'build_by_default': false,
2641   'install': false,
2644 default_mod_args = default_lib_args + {
2645   'name_prefix': '',
2646   'install_dir': dir_lib_pkg,
2649 default_bin_args = default_target_args + {
2650   'install_dir': dir_bin,
2653 if get_option('rpath')
2654   default_lib_args += {
2655     'install_rpath': ':'.join(lib_install_rpaths),
2656   }
2658   default_mod_args += {
2659     'install_rpath': ':'.join(mod_install_rpaths),
2660   }
2662   default_bin_args += {
2663     'install_rpath': ':'.join(bin_install_rpaths),
2664   }
2665 endif
2668 # Helper for exporting a limited number of symbols
2669 gen_export_kwargs = {
2670   'input': 'exports.txt',
2671   'output': '@BASENAME@.'+export_file_suffix,
2672   'command': [perl, files('src/tools/gen_export.pl'),
2673    '--format', export_file_format,
2674    '--input', '@INPUT0@', '--output', '@OUTPUT0@'],
2675   'build_by_default': false,
2676   'install': false,
2682 ### Helpers for custom targets used across the tree
2685 catalog_pm = files('src/backend/catalog/Catalog.pm')
2686 perfect_hash_pm = files('src/tools/PerfectHash.pm')
2687 gen_kwlist_deps = [perfect_hash_pm]
2688 gen_kwlist_cmd = [
2689   perl, '-I', '@SOURCE_ROOT@/src/tools',
2690   files('src/tools/gen_keywordlist.pl'),
2691   '--output', '@OUTDIR@', '@INPUT@']
2696 ### windows resources related stuff
2699 if host_system == 'windows'
2700   pg_ico = meson.source_root() / 'src' / 'port' / 'win32.ico'
2701   win32ver_rc = files('src/port/win32ver.rc')
2702   rcgen = find_program('src/tools/rcgen', native: true)
2704   rcgen_base_args = [
2705     '--srcdir', '@SOURCE_DIR@',
2706     '--builddir', meson.build_root(),
2707     '--rcout', '@OUTPUT0@',
2708     '--out', '@OUTPUT1@',
2709     '--input', '@INPUT@',
2710     '@EXTRA_ARGS@',
2711   ]
2713   if cc.get_argument_syntax() == 'msvc'
2714     rc = find_program('rc', required: true)
2715     rcgen_base_args += ['--rc', rc.path()]
2716     rcgen_outputs = ['@BASENAME@.rc', '@BASENAME@.res']
2717   else
2718     windres = find_program('windres', required: true)
2719     rcgen_base_args += ['--windres', windres.path()]
2720     rcgen_outputs = ['@BASENAME@.rc', '@BASENAME@.obj']
2721   endif
2723   # msbuild backend doesn't support this atm
2724   if meson.backend() == 'ninja'
2725     rcgen_base_args += ['--depfile', '@DEPFILE@']
2726   endif
2728   rcgen_bin_args = rcgen_base_args + [
2729     '--VFT_TYPE', 'VFT_APP',
2730     '--FILEENDING', 'exe',
2731     '--ICO', pg_ico
2732   ]
2734   rcgen_lib_args = rcgen_base_args + [
2735     '--VFT_TYPE', 'VFT_DLL',
2736     '--FILEENDING', 'dll',
2737   ]
2739   rc_bin_gen = generator(rcgen,
2740     depfile: '@BASENAME@.d',
2741     arguments: rcgen_bin_args,
2742     output: rcgen_outputs,
2743   )
2745   rc_lib_gen = generator(rcgen,
2746     depfile: '@BASENAME@.d',
2747     arguments: rcgen_lib_args,
2748     output: rcgen_outputs,
2749   )
2750 endif
2754 # headers that the whole build tree depends on
2755 generated_headers = []
2756 # headers that the backend build depends on
2757 generated_backend_headers = []
2758 # configure_files() output, needs a way of converting to file names
2759 configure_files = []
2761 # generated files that might conflict with a partial in-tree autoconf build
2762 generated_sources = []
2763 # same, for paths that differ between autoconf / meson builds
2764 # elements are [dir, [files]]
2765 generated_sources_ac = {}
2768 # First visit src/include - all targets creating headers are defined
2769 # within. That makes it easy to add the necessary dependencies for the
2770 # subsequent build steps.
2772 subdir('src/include')
2774 subdir('config')
2776 # Then through src/port and src/common, as most other things depend on them
2778 frontend_port_code = declare_dependency(
2779   compile_args: ['-DFRONTEND'],
2780   include_directories: [postgres_inc],
2781   dependencies: os_deps,
2784 backend_port_code = declare_dependency(
2785   compile_args: ['-DBUILDING_DLL'],
2786   include_directories: [postgres_inc],
2787   sources: [errcodes], # errcodes.h is needed due to use of ereport
2788   dependencies: os_deps,
2791 subdir('src/port')
2793 frontend_common_code = declare_dependency(
2794   compile_args: ['-DFRONTEND'],
2795   include_directories: [postgres_inc],
2796   sources: generated_headers,
2797   dependencies: [os_deps, zlib, zstd],
2800 backend_common_code = declare_dependency(
2801   compile_args: ['-DBUILDING_DLL'],
2802   include_directories: [postgres_inc],
2803   sources: generated_headers,
2804   dependencies: [os_deps, zlib, zstd],
2807 subdir('src/common')
2809 # all shared libraries should depend on shlib_code
2810 shlib_code = declare_dependency(
2811   link_args: ldflags_sl,
2814 # all static libraries not part of the backend should depend on this
2815 frontend_stlib_code = declare_dependency(
2816   include_directories: [postgres_inc],
2817   link_with: [common_static, pgport_static],
2818   sources: generated_headers,
2819   dependencies: [os_deps, libintl],
2822 # all shared libraries not part of the backend should depend on this
2823 frontend_shlib_code = declare_dependency(
2824   include_directories: [postgres_inc],
2825   link_with: [common_shlib, pgport_shlib],
2826   sources: generated_headers,
2827   dependencies: [shlib_code, os_deps, libintl],
2830 # Dependencies both for static and shared libpq
2831 libpq_deps += [
2832   thread_dep,
2834   gssapi,
2835   ldap_r,
2836   libintl,
2837   ssl,
2840 subdir('src/interfaces/libpq')
2841 # fe_utils depends on libpq
2842 subdir('src/fe_utils')
2844 # for frontend binaries
2845 frontend_code = declare_dependency(
2846   include_directories: [postgres_inc],
2847   link_with: [fe_utils, common_static, pgport_static],
2848   sources: generated_headers,
2849   dependencies: [os_deps, libintl],
2852 backend_both_deps += [
2853   thread_dep,
2854   bsd_auth,
2855   gssapi,
2856   icu,
2857   icu_i18n,
2858   ldap,
2859   libintl,
2860   libxml,
2861   lz4,
2862   pam,
2863   ssl,
2864   systemd,
2865   zlib,
2866   zstd,
2869 backend_mod_deps = backend_both_deps + os_deps
2871 backend_code = declare_dependency(
2872   compile_args: ['-DBUILDING_DLL'],
2873   include_directories: [postgres_inc],
2874   link_args: ldflags_be,
2875   link_with: [],
2876   sources: generated_headers + generated_backend_headers,
2877   dependencies: os_deps + backend_both_deps + backend_deps,
2880 # install these files only during test, not main install
2881 test_install_data = []
2882 test_install_libs = []
2884 # src/backend/meson.build defines backend_mod_code used for extension
2885 # libraries.
2888 # Then through the main sources. That way contrib can have dependencies on
2889 # main sources. Note that this explicitly doesn't enter src/test, right now a
2890 # few regression tests depend on contrib files.
2892 subdir('src')
2894 subdir('contrib')
2896 subdir('src/test')
2897 subdir('src/interfaces/libpq/test')
2898 subdir('src/interfaces/ecpg/test')
2900 subdir('doc/src/sgml')
2902 generated_sources_ac += {'': ['GNUmakefile']}
2904 # After processing src/test, add test_install_libs to the testprep_targets
2905 # to build them
2906 testprep_targets += test_install_libs
2909 # If there are any files in the source directory that we also generate in the
2910 # build directory, they might get preferred over the newly generated files,
2911 # e.g. because of a #include "file", which always will search in the current
2912 # directory first.
2913 message('checking for file conflicts between source and build directory')
2914 conflicting_files = []
2915 potentially_conflicting_files_t = []
2916 potentially_conflicting_files_t += generated_headers
2917 potentially_conflicting_files_t += generated_backend_headers
2918 potentially_conflicting_files_t += generated_backend_sources
2919 potentially_conflicting_files_t += generated_sources
2921 potentially_conflicting_files = []
2923 # convert all sources of potentially conflicting files into uniform shape
2924 foreach t : potentially_conflicting_files_t
2925   potentially_conflicting_files += t.full_path()
2926 endforeach
2927 foreach t : configure_files
2928   t = '@0@'.format(t)
2929   potentially_conflicting_files += meson.current_build_dir() / t
2930 endforeach
2931 foreach sub, fnames : generated_sources_ac
2932   sub = meson.build_root() / sub
2933   foreach fname : fnames
2934     potentially_conflicting_files += sub / fname
2935   endforeach
2936 endforeach
2938 # find and report conflicting files
2939 foreach build_path : potentially_conflicting_files
2940   build_path = host_system == 'windows' ? fs.as_posix(build_path) : build_path
2941   # str.replace is in 0.56
2942   src_path = meson.current_source_dir() / build_path.split(meson.current_build_dir() / '')[1]
2943   if fs.exists(src_path) or fs.is_symlink(src_path)
2944     conflicting_files += src_path
2945   endif
2946 endforeach
2947 # XXX: Perhaps we should generate a file that would clean these up? The list
2948 # can be long.
2949 if conflicting_files.length() > 0
2950   errmsg_cleanup = '''
2951 Conflicting files in source directory:
2952   @0@
2954 The conflicting files need to be removed, either by removing the files listed
2955 above, or by running configure and then make maintainer-clean.
2957   errmsg_cleanup = errmsg_cleanup.format(' '.join(conflicting_files))
2958   error(errmsg_nonclean_base.format(errmsg_cleanup))
2959 endif
2963 ###############################################################
2964 # Install targets
2965 ###############################################################
2968 # We want to define additional install targets beyond what meson provides. For
2969 # that we need to define targets depending on nearly everything. We collected
2970 # the results of i18n.gettext() invocations into nls_targets, that also
2971 # includes maintainer targets though. Collect the ones we want as a dependency.
2973 # i18n.gettext() doesn't return the dependencies before 0.60 - but the gettext
2974 # generation happens during install, so that's not a real issue.
2975 nls_mo_targets = []
2976 if libintl.found() and meson.version().version_compare('>=0.60')
2977   # use range() to avoid the flattening of the list that foreach() would do
2978   foreach off : range(0, nls_targets.length())
2979     # i18n.gettext() list containing 1) list of built .mo files 2) maintainer
2980     # -pot target 3) maintainer -pot target
2981     nls_mo_targets += nls_targets[off][0]
2982   endforeach
2983   alias_target('nls', nls_mo_targets)
2984 endif
2987 all_built = [
2988   backend_targets,
2989   bin_targets,
2990   libpq_st,
2991   pl_targets,
2992   contrib_targets,
2993   nls_mo_targets,
2994   testprep_targets,
2995   ecpg_targets,
2998 # Meson's default install target is quite verbose. Provide one that is quiet.
2999 install_quiet = custom_target('install-quiet',
3000   output: 'install-quiet',
3001   build_always_stale: true,
3002   build_by_default: false,
3003   command: [meson_bin, meson_args, 'install', '--quiet', '--no-rebuild'],
3004   depends: all_built,
3007 # Target to install files used for tests, which aren't installed by default
3008 install_test_files_args = [
3009   install_files,
3010   '--prefix', dir_prefix,
3011   '--install', contrib_data_dir, test_install_data,
3012   '--install', dir_lib_pkg, test_install_libs,
3014 run_target('install-test-files',
3015   command: [python] + install_test_files_args,
3016   depends: testprep_targets,
3021 ###############################################################
3022 # Test prep
3023 ###############################################################
3025 # DESTDIR for the installation we'll run tests in
3026 test_install_destdir = meson.build_root() / 'tmp_install/'
3028 # DESTDIR + prefix appropriately munged
3029 if build_system != 'windows'
3030   # On unixoid systems this is trivial, we just prepend the destdir
3031   assert(dir_prefix.startswith('/')) # enforced by meson
3032   test_install_location = '@0@@1@'.format(test_install_destdir, dir_prefix)
3033 else
3034   # drives, drive-relative paths, etc make this complicated on windows, call
3035   # into a copy of meson's logic for it
3036   command = [
3037     python, '-c',
3038     'import sys; from pathlib import PurePath; d1=sys.argv[1]; d2=sys.argv[2]; print(str(PurePath(d1, *PurePath(d2).parts[1:])))',
3039     test_install_destdir, dir_prefix]
3040   test_install_location = run_command(command, check: true).stdout().strip()
3041 endif
3043 meson_install_args = meson_args + ['install'] + {
3044     'meson': ['--quiet', '--only-changed', '--no-rebuild'],
3045     'muon': []
3046 }[meson_impl]
3048 # setup tests should be run first,
3049 # so define priority for these
3050 setup_tests_priority = 100
3051 test('tmp_install',
3052     meson_bin, args: meson_install_args ,
3053     env: {'DESTDIR':test_install_destdir},
3054     priority: setup_tests_priority,
3055     timeout: 300,
3056     is_parallel: false,
3057     suite: ['setup'])
3059 test('install_test_files',
3060     python,
3061     args: install_test_files_args + ['--destdir', test_install_destdir],
3062     priority: setup_tests_priority,
3063     is_parallel: false,
3064     suite: ['setup'])
3066 test_result_dir = meson.build_root() / 'testrun'
3069 # XXX: pg_regress doesn't assign unique ports on windows. To avoid the
3070 # inevitable conflicts from running tests in parallel, hackishly assign
3071 # different ports for different tests.
3073 testport = 40000
3075 test_env = environment()
3077 temp_install_bindir = test_install_location / get_option('bindir')
3078 test_env.set('PG_REGRESS', pg_regress.full_path())
3079 test_env.set('REGRESS_SHLIB', regress_module.full_path())
3081 # Test suites that are not safe by default but can be run if selected
3082 # by the user via the whitespace-separated list in variable PG_TEST_EXTRA.
3083 # Export PG_TEST_EXTRA so it can be checked in individual tap tests.
3084 test_env.set('PG_TEST_EXTRA', get_option('PG_TEST_EXTRA'))
3086 # Add the temporary installation to the library search path on platforms where
3087 # that works (everything but windows, basically). On windows everything
3088 # library-like gets installed into bindir, solving that issue.
3089 if library_path_var != ''
3090   test_env.prepend(library_path_var, test_install_location / get_option('libdir'))
3091 endif
3095 ###############################################################
3096 # Test Generation
3097 ###############################################################
3099 # When using a meson version understanding exclude_suites, define a
3100 # 'tmp_install' test setup (the default) that excludes tests running against a
3101 # pre-existing install and a 'running' setup that conflicts with creation of
3102 # the temporary installation and tap tests (which don't support running
3103 # against a running server).
3105 running_suites = []
3106 install_suites = []
3107 if meson.version().version_compare('>=0.57')
3108   runningcheck = true
3109 else
3110   runningcheck = false
3111 endif
3113 testwrap = files('src/tools/testwrap')
3115 foreach test_dir : tests
3116   testwrap_base = [
3117     testwrap,
3118     '--basedir', meson.build_root(),
3119     '--srcdir', test_dir['sd'],
3120   ]
3122   foreach kind, v : test_dir
3123     if kind in ['sd', 'bd', 'name']
3124       continue
3125     endif
3127     t = test_dir[kind]
3129     if kind in ['regress', 'isolation', 'ecpg']
3130       if kind == 'regress'
3131         runner = pg_regress
3132         fallback_dbname = 'regression_@0@'
3133       elif kind == 'isolation'
3134         runner = pg_isolation_regress
3135         fallback_dbname = 'isolation_regression_@0@'
3136       elif kind == 'ecpg'
3137         runner = pg_regress_ecpg
3138         fallback_dbname = 'ecpg_regression_@0@'
3139       endif
3141       test_group = test_dir['name']
3142       test_group_running = test_dir['name'] + '-running'
3144       test_output = test_result_dir / test_group / kind
3145       test_output_running = test_result_dir / test_group_running/ kind
3147       # Unless specified by the test, choose a non-conflicting database name,
3148       # to avoid conflicts when running against existing server.
3149       dbname = t.get('dbname',
3150         fallback_dbname.format(test_dir['name']))
3152       test_command_base = [
3153         runner.full_path(),
3154         '--inputdir', t.get('inputdir', test_dir['sd']),
3155         '--expecteddir', t.get('expecteddir', test_dir['sd']),
3156         '--bindir', '',
3157         '--dlpath', test_dir['bd'],
3158         '--max-concurrent-tests=20',
3159         '--dbname', dbname,
3160       ] + t.get('regress_args', [])
3162       test_selection = []
3163       if t.has_key('schedule')
3164         test_selection += ['--schedule', t['schedule'],]
3165       endif
3167       if kind == 'isolation'
3168         test_selection += t.get('specs', [])
3169       else
3170         test_selection += t.get('sql', [])
3171       endif
3173       env = test_env
3174       env.prepend('PATH', temp_install_bindir, test_dir['bd'])
3176       test_kwargs = {
3177         'protocol': 'tap',
3178         'priority': 10,
3179         'timeout': 1000,
3180         'depends': test_deps + t.get('deps', []),
3181         'env': env,
3182       } + t.get('test_kwargs', {})
3184       test(test_group / kind,
3185         python,
3186         args: [
3187           testwrap_base,
3188           '--testgroup', test_group,
3189           '--testname', kind,
3190           '--',
3191           test_command_base,
3192           '--outputdir', test_output,
3193           '--temp-instance', test_output / 'tmp_check',
3194           '--port', testport.to_string(),
3195           test_selection,
3196         ],
3197         suite: test_group,
3198         kwargs: test_kwargs,
3199       )
3200       install_suites += test_group
3202       # some tests can't support running against running DB
3203       if runningcheck and t.get('runningcheck', true)
3204         test(test_group_running / kind,
3205           python,
3206           args: [
3207             testwrap_base,
3208             '--testgroup', test_group_running,
3209             '--testname', kind,
3210             '--',
3211             test_command_base,
3212             '--outputdir', test_output_running,
3213             test_selection,
3214           ],
3215           is_parallel: t.get('runningcheck-parallel', true),
3216           suite: test_group_running,
3217           kwargs: test_kwargs,
3218         )
3219         running_suites += test_group_running
3220       endif
3222       testport += 1
3223     elif kind == 'tap'
3224       if not tap_tests_enabled
3225         continue
3226       endif
3228       test_command = [
3229         perl.path(),
3230         '-I', meson.source_root() / 'src/test/perl',
3231         '-I', test_dir['sd'],
3232       ]
3234       # Add temporary install, the build directory for non-installed binaries and
3235       # also test/ for non-installed test binaries built separately.
3236       env = test_env
3237       env.prepend('PATH', temp_install_bindir, test_dir['bd'], test_dir['bd'] / 'test')
3239       foreach name, value : t.get('env', {})
3240         env.set(name, value)
3241       endforeach
3243       test_group = test_dir['name']
3244       test_kwargs = {
3245         'protocol': 'tap',
3246         'suite': test_group,
3247         'timeout': 1000,
3248         'depends': test_deps + t.get('deps', []),
3249         'env': env,
3250       } + t.get('test_kwargs', {})
3252       foreach onetap : t['tests']
3253         # Make tap test names prettier, remove t/ and .pl
3254         onetap_p = onetap
3255         if onetap_p.startswith('t/')
3256           onetap_p = onetap.split('t/')[1]
3257         endif
3258         if onetap_p.endswith('.pl')
3259           onetap_p = fs.stem(onetap_p)
3260         endif
3262         test(test_dir['name'] / onetap_p,
3263           python,
3264           kwargs: test_kwargs,
3265           args: testwrap_base + [
3266             '--testgroup', test_dir['name'],
3267             '--testname', onetap_p,
3268             '--', test_command,
3269             test_dir['sd'] / onetap,
3270           ],
3271         )
3272       endforeach
3273       install_suites += test_group
3274     else
3275       error('unknown kind @0@ of test in @1@'.format(kind, test_dir['sd']))
3276     endif
3278   endforeach # kinds of tests
3280 endforeach # directories with tests
3282 # repeat condition so meson realizes version dependency
3283 if meson.version().version_compare('>=0.57')
3284   add_test_setup('tmp_install',
3285     is_default: true,
3286     exclude_suites: running_suites)
3287   add_test_setup('running',
3288     exclude_suites: ['setup'] + install_suites)
3289 endif
3293 ###############################################################
3294 # Pseudo targets
3295 ###############################################################
3297 alias_target('backend', backend_targets)
3298 alias_target('bin', bin_targets + [libpq_st])
3299 alias_target('pl', pl_targets)
3300 alias_target('contrib', contrib_targets)
3301 alias_target('testprep', testprep_targets)
3302 alias_target('install-world', install_quiet, installdocs)
3306 ###############################################################
3307 # The End, The End, My Friend
3308 ###############################################################
3310 if meson.version().version_compare('>=0.57')
3312   summary(
3313     {
3314       'data block size': '@0@ kB'.format(cdata.get('BLCKSZ') / 1024),
3315       'WAL block size': '@0@ kB'.format(cdata.get('XLOG_BLCKSZ') / 1024),
3316       'segment size': get_option('segsize_blocks') != 0 ?
3317         '@0@ blocks'.format(cdata.get('RELSEG_SIZE')) :
3318         '@0@ GB'.format(get_option('segsize')),
3319     },
3320     section: 'Data layout',
3321   )
3323   summary(
3324     {
3325       'host system': '@0@ @1@'.format(host_system, host_cpu),
3326       'build system': '@0@ @1@'.format(build_machine.system(),
3327                                        build_machine.cpu_family()),
3328     },
3329     section: 'System',
3330   )
3332   summary(
3333     {
3334       'linker': '@0@'.format(cc.get_linker_id()),
3335       'C compiler': '@0@ @1@'.format(cc.get_id(), cc.version()),
3336     },
3337     section: 'Compiler',
3338   )
3340   summary(
3341     {
3342       'CPP FLAGS': ' '.join(cppflags),
3343       'C FLAGS, functional': ' '.join(cflags),
3344       'C FLAGS, warnings': ' '.join(cflags_warn),
3345       'C FLAGS, modules': ' '.join(cflags_mod),
3346       'C FLAGS, user specified': ' '.join(get_option('c_args')),
3347       'LD FLAGS': ' '.join(ldflags + get_option('c_link_args')),
3348     },
3349     section: 'Compiler Flags',
3350   )
3352   if llvm.found()
3353     summary(
3354       {
3355         'C++ compiler': '@0@ @1@'.format(cpp.get_id(), cpp.version()),
3356       },
3357       section: 'Compiler',
3358     )
3360     summary(
3361       {
3362         'C++ FLAGS, functional': ' '.join(cxxflags),
3363         'C++ FLAGS, warnings': ' '.join(cxxflags_warn),
3364         'C++ FLAGS, user specified': ' '.join(get_option('cpp_args')),
3365       },
3366       section: 'Compiler Flags',
3367     )
3368   endif
3370   summary(
3371     {
3372       'bison': '@0@ @1@'.format(bison.full_path(), bison_version),
3373       'dtrace': dtrace,
3374     },
3375     section: 'Programs',
3376   )
3378   summary(
3379     {
3380       'bonjour': bonjour,
3381       'bsd_auth': bsd_auth,
3382       'docs': docs_dep,
3383       'docs_pdf': docs_pdf_dep,
3384       'gss': gssapi,
3385       'icu': icu,
3386       'ldap': ldap,
3387       'libxml': libxml,
3388       'libxslt': libxslt,
3389       'llvm': llvm,
3390       'lz4': lz4,
3391       'nls': libintl,
3392       'openssl': ssl,
3393       'pam': pam,
3394       'plperl': perl_dep,
3395       'plpython': python3_dep,
3396       'pltcl': tcl_dep,
3397       'readline': readline,
3398       'selinux': selinux,
3399       'systemd': systemd,
3400       'uuid': uuid,
3401       'zlib': zlib,
3402       'zstd': zstd,
3403     },
3404     section: 'External libraries',
3405   )
3407 endif